Compare commits

..

14 Commits

Author SHA1 Message Date
barisusakli
1943f8fdfa closes #2370 2014-11-08 17:53:03 -05:00
barisusakli
6ae0ca17d7 closes #2354 2014-11-08 15:29:40 -05:00
Julian Lam
727010822d 0.5.4 2014-11-07 15:06:34 -05:00
Julian Lam
a1bf407cd4 fixed client-side so semver doesn't explode when nbbpm returns 'latest', fixed tests so they pass again, #2363 2014-11-07 15:04:15 -05:00
Julian Lam
a96eb0c2a4 updating shrinkwrap file 2014-11-07 14:58:21 -05:00
Julian Lam
5ddbffb083 plugin installation takes a version now, and queries nbbpm, #2363
better error handling for #2363

upgrades now also call the suggestion route from nbbpm

upgrade button will show alert now if you try to upgrade past the suggested version, #2363
2014-11-07 14:45:54 -05:00
barisusakli
f740a2d958 dont display email/fullname if they are private 2014-11-05 23:59:20 -05:00
Julian Lam
7c77c8cfc6 why does this file keep disappearing... :@ 2014-11-05 20:16:48 -05:00
Julian Lam
f19871f311 closed #2349, removed use of deprecated -d flag in executable 2014-11-04 18:32:59 -05:00
barisusakli
50191a38b3 forkWorker function, pipe new worker output to log 2014-11-04 18:32:47 -05:00
barisusakli
37f1f94bed added stderr piping to output 2014-11-04 18:32:42 -05:00
barisusakli
4cdb3a34d2 log signal as well 2014-11-04 18:32:34 -05:00
barisusakli
cce076fc83 closes #2323 2014-10-30 14:30:10 -04:00
Julian Lam
0a73fade08 updated version for 0.5.3 2014-10-27 17:36:26 -04:00
4409 changed files with 64981 additions and 221177 deletions

View File

@@ -1,22 +0,0 @@
# Save as .codeclimate.yml (note leading .) in project root directory
version: "2"
languages:
Ruby: false
JavaScript: true
PHP: false
checks:
file-lines:
config:
threshold: 500
method-lines:
config:
threshold: 50
method-complexity:
config:
threshold: 10
similar-code:
config:
threshold: 65
exclude_paths:
- "public/vendor/*"
- "test/*"

View File

@@ -1,8 +1,4 @@
root = true root = true
[{*.js, *.css, *.tpl, *.json}] [*.js, *.css, *.tpl]
indent_style = tab indent_style = tab
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = false

View File

@@ -1,21 +0,0 @@
node_modules/
*.sublime-project
*.sublime-workspace
.project
.vagrant
.DS_Store
logs/
/public/templates
/public/uploads
/public/sounds
/public/vendor
/public/src/modules/string.js
.idea/
.vscode/
*.ipr
*.iws
/coverage
/build
.eslintrc
test/files
*.min.js

140
.eslintrc
View File

@@ -1,140 +0,0 @@
{
"extends": "airbnb-base",
"parserOptions": {
"sourceType": "script"
},
"rules": {
// Customized
"handle-callback-err": [ "error","^(e$|(e|(.*(_e|E)))rr)" ],
"comma-dangle": ["error", {
"arrays": "always-multiline",
"objects": "always-multiline",
"imports": "always-multiline",
"exports": "always-multiline",
"functions": "never"
}],
"no-return-await": "off",
"no-constant-condition": "off",
"no-empty": ["error", { "allowEmptyCatch": true }],
"no-underscore-dangle": "off",
"no-console": "off",
"no-mixed-operators": ["error", { "allowSamePrecedence": true }],
"strict": ["error", "global"],
"consistent-return": "off",
"func-names": "off",
"no-tabs": "off",
"indent": ["error", "tab"],
"no-eq-null": "off",
"camelcase": "off",
"no-new": "off",
"no-shadow": "off",
"no-use-before-define": ["error", "nofunc"],
"no-prototype-builtins": "off",
"new-cap": "off",
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
"object-curly-newline": "off",
"no-restricted-globals": "off",
"function-paren-newline": "off",
"import/no-unresolved": "error",
"quotes": ["error", "single", {
"avoidEscape": true,
"allowTemplateLiterals": true
}],
"no-else-return": [ "error", { "allowElseIf": true } ],
"operator-linebreak": [ "error", "after" ],
"arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }],
// ES6
"prefer-rest-params": "off",
"prefer-spread": "off",
"prefer-arrow-callback": "off",
"prefer-template": "off",
"no-var": "off",
"object-shorthand": "off",
"vars-on-top": "off",
"prefer-destructuring": "off",
// TODO
"import/no-extraneous-dependencies": "off",
"import/no-dynamic-require": "off",
"import/newline-after-import": "off",
"no-bitwise": "off",
"global-require": "off",
"max-len": "off",
"no-param-reassign": "off",
"no-restricted-syntax": "off",
"no-script-url": "off",
"default-case": "off",
"linebreak-style": "off",
// "no-multi-assign": "off",
// "one-var": "off",
// "no-undef": "off",
// "max-nested-callbacks": "off",
// "no-mixed-requires": "off",
// "brace-style": "off",
// "max-statements-per-line": "off",
// "no-unused-vars": "off",
// "no-mixed-spaces-and-tabs": "off",
// "no-useless-concat": "off",
// "require-jsdoc": "off",
// "eqeqeq": "off",
// "no-negated-condition": "off",
// "one-var-declaration-per-line": "off",
// "no-lonely-if": "off",
// "radix": "off",
// "no-else-return": "off",
// "no-useless-escape": "off",
// "block-scoped-var": "off",
// "operator-assignment": "off",
// "yoda": "off",
// "no-loop-func": "off",
// "no-void": "off",
// "valid-jsdoc": "off",
// "no-cond-assign": "off",
// "no-redeclare": "off",
// "no-unreachable": "off",
// "no-nested-ternary": "off",
// "operator-linebreak": "off",
// "guard-for-in": "off",
// "no-unneeded-ternary": "off",
// "no-sequences": "off",
// "no-extend-native": "off",
// "no-shadow-restricted-names": "off",
// "no-extra-boolean-cast": "off",
// "no-path-concat": "off",
// "no-unused-expressions": "off",
// "no-return-assign": "off",
// "no-restricted-modules": "off",
// "object-curly-spacing": "off",
// "indent": "off",
// "padded-blocks": "off",
// "eol-last": "off",
// "lines-around-directive": "off",
// "strict": "off",
// "comma-dangle": "off",
// "no-multi-spaces": "off",
// "quotes": "off",
// "keyword-spacing": "off",
// "no-mixed-operators": "off",
// "comma-spacing": "off",
// "no-trailing-spaces": "off",
// "key-spacing": "off",
// "no-multiple-empty-lines": "off",
// "spaced-comment": "off",
// "space-in-parens": "off",
// "block-spacing": "off",
// "quote-props": "off",
// "space-unary-ops": "off",
// "no-empty": "off",
// "dot-notation": "off",
// "func-call-spacing": "off",
// "array-bracket-spacing": "off",
// "object-property-newline": "off",
// "no-continue": "off",
// "no-extra-semi": "off",
// "no-spaced-func": "off",
// "no-useless-return": "off"
}
}

13
.gitattributes vendored
View File

@@ -1,13 +0,0 @@
# These files are text and should be normalized (convert crlf => lf)
*.json text
*.css text
*.less text
*.tpl text
*.html text
*.js text
*.md text
# Images should be treated as binary
# (binary is a macro for -text -diff)
*.png binary
*.jpg binary

View File

@@ -1,69 +0,0 @@
# Submitting a Pull Request to NodeBB?
First of all, thank you! Before submission, please run `npm test` to lint and run the automated NodeBB tests. If everything passes, you're good to go. If you have any errors, please fix them and re-run `npm test` to make sure there aren't any others.
## Styleguide and linting
NodeBB mostly conforms to the [AirBnB Javascript style guide](https://github.com/airbnb/javascript#readme). If you're running into a lot of ESlint errors, you may want to install an editor plugin to display them in real time.
## Contributor License Agreement
Thank you for considering contributing to NodeBB. **Before you are able to submit a pull request, please take a moment to read our [contributor license agreement](https://gist.github.com/psychobunny/65946d7aa8854b12fab9)** and agree to it on the pull request page on GitHub. In summary, signing this document means that 1) you own the code that you are contributing and 2) you give permission to NodeBB Inc. to license the code to others. This agreement applies to any repository under the NodeBB organization.
If you are writing contributions as part of employment from another company / individual, then your employer will need to sign a separate agreement. Please [contact us](mailto:accounts@nodebb.org) so that we can send this additional agreement to your employer.
# Having problems installing NodeBB?
Chances are somebody has run into this problem before. After consulting our [documentation](https://docs.nodebb.org/installing/os/), please head over to our [community support forum](https://community.nodebb.org) for advice.
# Found a Security Vulnerability?
If you believe you have identified a security vulnerability with NodeBB, report it as soon as possible via email to **security@nodebb.org**.
A member of the NodeBB security team will respond to the issue.
Please do not post it to the public bug tracker.
# Issues & Bugs
Thanks for reporting an issue with NodeBB! Please follow these guidelines in order to streamline the debugging process. The more guidelines you follow, the easier it will be for us to reproduce your problem.
In general, if we can't reproduce it, we can't fix it!
## Try the latest version of NodeBB
There is a chance that the issue you are experiencing may have already been fixed.
## Provide the NodeBB version number and git hash
You can find the NodeBB version number in the Admin Control Panel (ACP), as well as the first line output to the shell when running NodeBB
``` plaintext
3/4 12:38:57 [10752] - info: NodeBB v1.4.5 Copyright (C) 2013-2017 NodeBB Inc.
3/4 12:38:57 [10752] - info: This program comes with ABSOLUTELY NO WARRANTY.
3/4 12:38:57 [10752] - info: This is free software, and you are welcome to redistribute it under certain conditions.
```
If you are running NodeBB via git, it is also helpful to let the maintainers know what commit hash you are on. To find the commit hash, execute the following command:
``` bash
$ cd /path/to/my/nodebb
$ git rev-parse HEAD
```
If you have downloaded the `.zip` or `.tar.gz` packages from GitHub (or elsewhere), please let us know.
## Provide theme versions if issue is related to the theme/display
Use `npm ls` to list the versions of the theme you're using. In this example, we're running the Persona theme, which depends on the Vanilla theme.
``` bash
$ npm ls nodebb-theme-vanilla nodebb-theme-persona
nodebb@1.4.3 /path/to/nodebb
+-- nodebb-theme-persona@4.2.4
`-- nodebb-theme-vanilla@5.2.0
```
## Attempt to use `git bisect`
If you have installed NodeBB via GitHub clone, are familiar with utilising git, and are willing to help us narrow down the specific commit that causes a bug, consider running `git bisect`.
A full guide can be found here: [Debugging with Git/Binary Search](http://git-scm.com/book/en/Git-Tools-Debugging-with-Git#Binary-Search)

View File

@@ -1,28 +0,0 @@
<!--
== Github Issues are for bug reports and feature requests only ==
== Please visit https://community.nodebb.org for other support ==
== Found a security exploit? Please email us at security@nodebb.org instead for immediate attention ==
== → DO NOT SUBMIT VULNERABILITIES TO THE PUBLIC BUG TRACKER ==
-->
<!-- ++ Please include the following information when submitting a bug report ++ -->
- **NodeBB version:**
- **NodeBB git hash:**
<!-- (to find your git hash, execute `git rev-parse HEAD` from the main NodeBB directory) -->
- **Installed NodeBB Plugins:**
<!-- (to find installed plugins run ./nodebb plugins) -->
- **Database type:** mongo, redis, or postgres
- **Database version:**
<!-- `mongod --version`, `redis-server --version`, or `postgres --version` -->
- **Exact steps to cause this issue:**
<!--
1. First I did this...
2. Then, I clicked on this item...
-->
- **What you expected:**
<!-- e.g. I expected *abc* to *xyz* -->
- **What happened instead:**
<!-- e.g. Instead, I got *zyx* and NodeBB set fire to my house -->
<!-- Thank you! -->

42
.gitignore vendored
View File

@@ -1,15 +1,14 @@
yarn.lock
npm-debug.log npm-debug.log
node_modules/ node_modules/
sftp-config.json sftp-config.json
config.json config.json
jsconfig.json
public/src/nodebb.min.js public/src/nodebb.min.js
!src/views/config.json !src/views/config.json
public/css/*.css public/css/*.css
*.sublime-project *.sublime-project
*.sublime-workspace *.sublime-workspace
.project .project
.idea
*.swp *.swp
Vagrantfile Vagrantfile
.vagrant .vagrant
@@ -17,52 +16,21 @@ provision.sh
*.komodoproject *.komodoproject
.DS_Store .DS_Store
feeds/recent.rss feeds/recent.rss
.eslintcache
.svn
logs/ # winston?
error.log
events.log
pidfile pidfile
# templates # templates
/public/templates /public/templates
/public/sounds /public/sounds
/public/uploads /public/uploads
/test/uploads
# compiled files # compiled files
/public/stylesheet.css /public/stylesheet.css
/public/admin.css /public/admin.css
/public/nodebb.min.js /public/nodebb.min.js
/public/nodebb.min.js.map /public/nodebb.min.js.map
/public/acp.min.js
/public/acp.min.js.map
/public/installer.css
/public/installer.min.js
/public/logo.png
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio
*.iml
## Directory-based project format:
.idea/
.vscode/
## File-based project format:
*.ipr
*.iws
## Transifex
tx.exe
.transifexrc
##Coverage output
coverage
.nyc_output
*.log
test/files/normalise.jpg.png
test/files/normalise-resized.jpg
package-lock.json
/package.json

View File

@@ -4,8 +4,6 @@
"maxerr" : 50, // {int} Maximum error before stopping "maxerr" : 50, // {int} Maximum error before stopping
"esversion": 6,
// Enforcing // Enforcing
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase "camelcase" : false, // true: Identifiers must be in camelCase
@@ -26,7 +24,7 @@
// "single" : require single quotes // "single" : require single quotes
// "double" : require double quotes // "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : true, // true: Require all defined variables be used "unused" : false, // true: Require all defined variables be used TODO: Set this to true, update codebase.
"strict" : true, // true: Requires all functions run in ES5 Strict Mode "strict" : true, // true: Requires all functions run in ES5 Strict Mode
"trailing" : false, // true: Prohibit trailing whitespaces "trailing" : false, // true: Prohibit trailing whitespaces
"maxparams" : false, // {int} Max number of formal params allowed per function "maxparams" : false, // {int} Max number of formal params allowed per function
@@ -76,7 +74,6 @@
"worker" : false, // Web Workers "worker" : false, // Web Workers
"wsh" : false, // Windows Scripting Host "wsh" : false, // Windows Scripting Host
"yui" : false, // Yahoo User Interface "yui" : false, // Yahoo User Interface
"mocha": true,
// Legacy // Legacy
"nomen" : false, // true: Prohibit dangling `_` in variables "nomen" : false, // true: Prohibit dangling `_` in variables
@@ -85,7 +82,5 @@
"white" : false, // true: Check against strict whitespace and indentation rules "white" : false, // true: Check against strict whitespace and indentation rules
// Custom Globals // Custom Globals
"globals" : { "globals" : {} // additional predefined global variables
"Promise": true
} // additional predefined global variables
} }

View File

@@ -1,47 +1,12 @@
services: services:
- mongodb
- redis-server - redis-server
- postgresql
before_install: before_install:
- cp install/package.json package.json - npm i --production
- sudo apt-get update - node app --setup="{\"base_url\":\"http://127.0.0.1\",\"port\":4567,\"use_port\":false,\"secret\":\"abcdef\",\"bind_address\":\"0.0.0.0\",\"database\":\"redis\",\"redis:host\":\"127.0.0.1\",\"redis:port\":6379,\"redis:password\":\"\",\"redis:database\":0,\"admin:username\":\"admin\",\"admin:email\":\"test@example.org\",\"admin:password\":\"abcdef\",\"admin:password:confirm\":\"abcdef\"}" --ci="{\"host\":\"127.0.0.1\",\"port\":6379,\"database\":0}"
- sudo apt-get --yes remove postgresql\*
- sudo apt-get install -y postgresql-12 postgresql-client-12
- sudo cp /etc/postgresql/{9.6,12}/main/pg_hba.conf
- sudo service postgresql restart 12
before_script:
- sleep 15 # wait for mongodb to be ready
- "mongo mydb_test --eval 'db.createUser({user:\"travis\", pwd: \"test\", roles: []});'"
- sh -c "if [ '$DB' = 'mongodb' ]; then node app --setup=\"{\\\"url\\\":\\\"http://127.0.0.1:4567\\\",\\\"secret\\\":\\\"abcdef\\\",\\\"database\\\":\\\"mongo\\\",\\\"mongo:host\\\":\\\"127.0.0.1\\\",\\\"mongo:port\\\":27017,\\\"mongo:username\\\":\\\"\\\",\\\"mongo:password\\\":\\\"\\\",\\\"mongo:database\\\":0,\\\"admin:username\\\":\\\"admin\\\",\\\"admin:email\\\":\\\"test@example.org\\\",\\\"admin:password\\\":\\\"hAN3Eg8W\\\",\\\"admin:password:confirm\\\":\\\"hAN3Eg8W\\\"}\" --ci=\"{\\\"host\\\":\\\"127.0.0.1\\\",\\\"port\\\":27017,\\\"database\\\":\\\"travis_ci_test\\\"}\"; fi"
- sh -c "if [ '$DB' = 'redis' ]; then node app --setup=\"{\\\"url\\\":\\\"http://127.0.0.1:4567/forum\\\",\\\"secret\\\":\\\"abcdef\\\",\\\"database\\\":\\\"redis\\\",\\\"redis:host\\\":\\\"127.0.0.1\\\",\\\"redis:port\\\":6379,\\\"redis:password\\\":\\\"\\\",\\\"redis:database\\\":0,\\\"admin:username\\\":\\\"admin\\\",\\\"admin:email\\\":\\\"test@example.org\\\",\\\"admin:password\\\":\\\"hAN3Eg8W\\\",\\\"admin:password:confirm\\\":\\\"hAN3Eg8W\\\"}\" --ci=\"{\\\"host\\\":\\\"127.0.0.1\\\",\\\"port\\\":6379,\\\"database\\\":1}\"; fi"
- sh -c "if [ '$DB' = 'postgres' ]; then psql -c 'create database nodebb;' -U postgres; psql -c 'create database travis_ci_test;' -U postgres; node app --setup=\"{\\\"url\\\":\\\"http://127.0.0.1:4567\\\",\\\"secret\\\":\\\"abcdef\\\",\\\"database\\\":\\\"postgres\\\",\\\"postgres:host\\\":\\\"127.0.0.1\\\",\\\"postgres:port\\\":5432,\\\"postgres:password\\\":\\\"\\\",\\\"postgres:database\\\":\\\"nodebb\\\",\\\"admin:username\\\":\\\"admin\\\",\\\"admin:email\\\":\\\"test@example.org\\\",\\\"admin:password\\\":\\\"hAN3Eg8W\\\",\\\"admin:password:confirm\\\":\\\"hAN3Eg8W\\\"}\" --ci=\"{\\\"host\\\":\\\"127.0.0.1\\\",\\\"port\\\":5432,\\\"username\\\":\\\"postgres\\\",\\\"database\\\":\\\"travis_ci_test\\\"}\"; fi"
after_success:
- "npm run coveralls"
language: node_js language: node_js
sudo: false
dist: xenial
env:
global:
- PGUSER=postgres
- PGPORT=5432
- CXX=g++-4.8
jobs:
- "DB=mongodb TEST_ENV=production"
- "DB=mongodb TEST_ENV=development"
- "DB=redis TEST_ENV=production"
- "DB=postgres TEST_ENV=production"
addons:
apt:
sources:
- ubuntu-toolchain-r-test
- mongodb-4.0-xenial
packages:
- g++-4.8
- mongodb-org-server
node_js: node_js:
- "12" - "0.11"
- "10" - "0.10"
branches: branches:
only: only:
- master - master
- develop

3100
.tx/config

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

41
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,41 @@
# Issues & Bugs
Thanks for reporting an issue with NodeBB! Please follow these guidelines in order to streamline the debugging process. The more guidelines you follow, the easier it will be for us to reproduce your problem.
In general, if we can't reproduce it, we can't fix it!
> #### **Important**
> If you believe you have identified a security vulnerability with NodeBB, report it as soon as possible via email to **security@nodebb.org**.
> A member of the NodeBB security team will respond to the issue.
> Please do not post it to the public bug tracker.
## Try the latest version of NodeBB
There is a chance that the issue you are experiencing may have already been fixed.
## Provide the NodeBB version number and git hash
You can find the NodeBB version number in the Admin Control Panel (ACP), as well as the first line output to the shell when running NodeBB
``` plaintext
info: NodeBB v0.5.2-dev Copyright (C) 2013-2014 NodeBB Inc.
info: This program comes with ABSOLUTELY NO WARRANTY.
info: This is free software, and you are welcome to redistribute it under certain conditions.
info:
info: Time: Tue Oct 07 2014 20:25:20 GMT-0400 (EDT)
```
If you are running NodeBB via git, it is also helpful to let the maintainers know what commit hash you are on. To find the commit hash, execute the following command:
``` bash
$ cd /path/to/my/nodebb
$ git rev-parse HEAD
```
If you have downloaded the `.zip` or `.tar.gz` packages from GitHub (or elsewhere), please let us know.
## Attempt to use `git bisect`
If you have installed NodeBB via GitHub clone, are familiar with utilising git, and are willing to help us narrow down the specific commit that causes a bug, consider running `git bisect`.
A full guide can be found here: [Debugging with Git/Binary Search](http://git-scm.com/book/en/Git-Tools-Debugging-with-Git#Binary-Search)

View File

@@ -1,23 +0,0 @@
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 \
daemon=false \
silent=false
EXPOSE 4567
CMD ./nodebb start

View File

@@ -1,246 +0,0 @@
'use strict';
var async = require('async');
var fork = require('child_process').fork;
var env = process.env;
var worker;
var updateWorker;
var initWorker;
var incomplete = [];
var running = 0;
env.NODE_ENV = env.NODE_ENV || 'development';
var nconf = require('nconf');
nconf.file({
file: 'config.json',
});
nconf.defaults({
base_dir: __dirname,
views_dir: './build/public/templates',
});
var winston = require('winston');
winston.configure({
transports: [
new winston.transports.Console({
handleExceptions: true,
}),
],
});
var db = require('./src/database');
module.exports = function (grunt) {
var args = [];
var initArgs = ['--build'];
if (!grunt.option('verbose')) {
args.push('--log-level=info');
initArgs.push('--log-level=info');
}
function update(action, filepath, target) {
var updateArgs = args.slice();
var compiling;
var time = Date.now();
if (target === 'lessUpdated_Client') {
compiling = 'clientCSS';
} else if (target === 'lessUpdated_Admin') {
compiling = 'acpCSS';
} else if (target === 'clientUpdated') {
compiling = 'js';
} else if (target === 'templatesUpdated') {
compiling = 'tpl';
} else if (target === 'langUpdated') {
compiling = 'lang';
} else if (target === 'serverUpdated') {
// Do nothing, just restart
}
if (compiling && !incomplete.includes(compiling)) {
incomplete.push(compiling);
}
updateArgs.push('--build');
updateArgs.push(incomplete.join(','));
worker.kill();
if (updateWorker) {
updateWorker.kill('SIGKILL');
}
updateWorker = fork('app.js', updateArgs, { env: env });
running += 1;
updateWorker.on('exit', function () {
running -= 1;
if (running === 0) {
worker = fork('app.js', args, {
env: env,
});
worker.on('message', function () {
if (incomplete.length) {
incomplete = [];
if (grunt.option('verbose')) {
grunt.log.writeln('NodeBB restarted in ' + (Date.now() - time) + ' ms');
}
}
});
}
});
}
grunt.initConfig({
watch: {},
});
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.registerTask('default', ['watch']);
grunt.registerTask('init', function () {
var done = this.async();
async.waterfall([
function (next) {
db.init(next);
},
function (next) {
db.getSortedSetRange('plugins:active', 0, -1, next);
},
function (plugins, next) {
addBaseThemes(plugins, next);
},
function (plugins, next) {
if (!plugins.includes('nodebb-plugin-composer-default')) {
plugins.push('nodebb-plugin-composer-default');
}
if (process.argv.includes('--core')) {
plugins = [];
}
const lessUpdated_Client = plugins.map(p => 'node_modules/' + p + '/**/*.less');
const lessUpdated_Admin = plugins.map(p => 'node_modules/' + p + '/**/*.less');
const clientUpdated = plugins.map(p => 'node_modules/' + p + '/**/*.js');
const templatesUpdated = plugins.map(p => 'node_modules/' + p + '/**/*.tpl');
const langUpdated = plugins.map(p => 'node_modules/' + p + '/**/*.json');
grunt.config(['watch'], {
lessUpdated_Client: {
files: [
'public/less/*.less',
'!public/less/admin/**/*.less',
...lessUpdated_Client,
'!node_modules/nodebb-*/node_modules/**',
'!node_modules/nodebb-*/.git/**',
],
options: {
interval: 1000,
},
},
lessUpdated_Admin: {
files: [
'public/less/admin/**/*.less',
...lessUpdated_Admin,
'!node_modules/nodebb-*/node_modules/**',
'!node_modules/nodebb-*/.git/**',
],
options: {
interval: 1000,
},
},
clientUpdated: {
files: [
'public/src/**/*.js',
...clientUpdated,
'!node_modules/nodebb-*/node_modules/**',
'node_modules/benchpressjs/build/benchpress.js',
'!node_modules/nodebb-*/.git/**',
],
options: {
interval: 1000,
},
},
serverUpdated: {
files: ['*.js', 'install/*.js', 'src/**/*.js', '!src/upgrades/**'],
options: {
interval: 1000,
},
},
templatesUpdated: {
files: [
'src/views/**/*.tpl',
...templatesUpdated,
'!node_modules/nodebb-*/node_modules/**',
'!node_modules/nodebb-*/.git/**',
],
options: {
interval: 1000,
},
},
langUpdated: {
files: [
'public/language/en-GB/*.json',
'public/language/en-GB/**/*.json',
...langUpdated,
'!node_modules/nodebb-*/node_modules/**',
'!node_modules/nodebb-*/.git/**',
'!node_modules/nodebb-*/plugin.json',
'!node_modules/nodebb-*/package.json',
'!node_modules/nodebb-*/theme.json',
],
options: {
interval: 1000,
},
},
});
next();
},
], done);
});
grunt.task.run('init');
env.NODE_ENV = 'development';
if (grunt.option('skip')) {
worker = fork('app.js', args, {
env: env,
});
} else {
initWorker = fork('app.js', initArgs, {
env: env,
});
initWorker.on('exit', function () {
worker = fork('app.js', args, {
env: env,
});
});
}
grunt.event.on('watch', update);
};
function addBaseThemes(plugins, callback) {
const themeId = plugins.find(p => p.startsWith('nodebb-theme-'));
if (!themeId) {
return setImmediate(callback, null, plugins);
}
function getBaseRecursive(themeId) {
try {
const baseTheme = require(themeId + '/theme').baseTheme;
if (baseTheme) {
plugins.push(baseTheme);
getBaseRecursive(baseTheme);
}
} catch (err) {
console.log(err);
}
}
getBaseRecursive(themeId);
callback(null, plugins);
}

View File

@@ -1,46 +1,34 @@
# <img alt="NodeBB" src="http://i.imgur.com/mYxPPtB.png" /> # <img alt="NodeBB" src="http://i.imgur.com/mYxPPtB.png" />
[![Build Status](https://travis-ci.org/NodeBB/NodeBB.svg?branch=master)](https://travis-ci.org/NodeBB/NodeBB) [![Build Status](https://travis-ci.org/NodeBB/NodeBB.svg?branch=master)](https://travis-ci.org/NodeBB/NodeBB)
[![Coverage Status](https://coveralls.io/repos/github/NodeBB/NodeBB/badge.svg?branch=master)](https://coveralls.io/github/NodeBB/NodeBB?branch=master) [![Dependency Status](https://david-dm.org/nodebb/nodebb.svg)](https://david-dm.org/nodebb/nodebb)
[![Dependency Status](https://david-dm.org/nodebb/nodebb.svg?path=install)](https://david-dm.org/nodebb/nodebb?path=install)
[![Code Climate](https://codeclimate.com/github/NodeBB/NodeBB/badges/gpa.svg)](https://codeclimate.com/github/NodeBB/NodeBB) [![Code Climate](https://codeclimate.com/github/NodeBB/NodeBB/badges/gpa.svg)](https://codeclimate.com/github/NodeBB/NodeBB)
[![Documentation Status](https://readthedocs.org/projects/nodebb/badge/?version=latest)](https://readthedocs.org/projects/nodebb/?badge=latest)
[**NodeBB Forum Software**](https://nodebb.org) is powered by Node.js and built on either a Redis or MongoDB database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB has many modern features out of the box such as social network integration and streaming discussions, while still making sure to be compatible with older browsers. **NodeBB Forum Software** is powered by Node.js and built on a Redis database. It utilizes web sockets for instant interactions and real-time notifications. NodeBB is compatible down to IE8 and has many modern features out of the box such as social network integration and streaming discussions.
Additional functionality is enabled through the use of third-party plugins.
* [Get NodeBB](http://www.nodebb.org/ "NodeBB")
* [Demo & Meta Discussion](http://community.nodebb.org) * [Demo & Meta Discussion](http://community.nodebb.org)
* [Documentation & Installation Instructions](http://docs.nodebb.org)
* [Help translate NodeBB](https://www.transifex.com/projects/p/nodebb/)
* [NodeBB Blog](http://blog.nodebb.org) * [NodeBB Blog](http://blog.nodebb.org)
* [Premium Hosting for NodeBB](http://www.nodebb.org/ "NodeBB") * [Documentation](http://docs.nodebb.org) - includes setup instructions for other platforms
* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode
* [Follow us on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") * [Follow us on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook") * [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
* [Get Plugins](http://community.nodebb.org/category/7/nodebb-plugins "NodeBB Plugins")
* [Get Themes](http://community.nodebb.org/category/10/nodebb-themes "NodeBB Themes")
* [Help translate NodeBB](https://www.transifex.com/projects/p/nodebb/)
## Screenshots ## Screenshots
NodeBB's theming engine is highly flexible and does not restrict your design choices. Check out some themed installs in these screenshots below: [<img src="http://i.imgur.com/FLOUuIqb.png" />](http://i.imgur.com/FLOUuIq.png)&nbsp;[<img src="http://i.imgur.com/Ud1LrfIb.png" />](http://i.imgur.com/Ud1LrfI.png)&nbsp;[<img src="http://i.imgur.com/ZC8W39ab.png" />](http://i.imgur.com/ZC8W39a.png)&nbsp;[<img src="http://i.imgur.com/o90kVPib.png" />](http://i.imgur.com/o90kVPi.png)&nbsp;[<img src="http://i.imgur.com/AaRRrU2b.png" />](http://i.imgur.com/AaRRrU2.png)&nbsp;[<img src="http://i.imgur.com/LmHtPhob.png" />](http://i.imgur.com/LmHtPho.png)&nbsp;[<img src="http://i.imgur.com/paiJPJkb.jpg" />](http://i.imgur.com/paiJPJk.jpg)&nbsp;[<img src="http://i.imgur.com/ZfavPHDb.png" />](http://i.imgur.com/ZfavPHD.png)&nbsp;[<img src="http://i.imgur.com/8OLssij.png" />](http://i.imgur.com/8OLssij.png)&nbsp;[<img src="http://i.imgur.com/JKOc0LZ.png"/>](http://i.imgur.com/JKOc0LZ.png)
[![](http://i.imgur.com/VCoOFyqb.png)](http://i.imgur.com/VCoOFyq.png)
[![](http://i.imgur.com/FLOUuIqb.png)](http://i.imgur.com/FLOUuIq.png)
[![](http://i.imgur.com/Ud1LrfIb.png)](http://i.imgur.com/Ud1LrfI.png)
[![](http://i.imgur.com/h6yZ66sb.png)](http://i.imgur.com/h6yZ66s.png)
[![](http://i.imgur.com/o90kVPib.png)](http://i.imgur.com/o90kVPi.png)
[![](http://i.imgur.com/AaRRrU2b.png)](http://i.imgur.com/AaRRrU2.png)
[![](http://i.imgur.com/LmHtPhob.png)](http://i.imgur.com/LmHtPho.png)
[![](http://i.imgur.com/paiJPJkb.jpg)](http://i.imgur.com/paiJPJk.jpg)
Our minimalist "Persona" theme gets you going right away, no coding experience required.
[![](http://i.imgur.com/HwNEXGu.png)](http://i.imgur.com/HwNEXGu.png)
[![](http://i.imgur.com/II1byYs.png)](http://i.imgur.com/II1byYs.png)
Credit: [Convoe](http://www.convoe.com), [Kano](http://www.kano.me), [Manchester United Forum](http://manutdforums.com/).
## How can I follow along/contribute? ## How can I follow along/contribute?
* Our feature roadmap is hosted on the project wiki's [Version History / Roadmap](https://github.com/NodeBB/NodeBB/wiki/Version-History-%26-Roadmap)
* 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 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 are a designer, [NodeBB needs themes](http://community.nodebb.org/category/10/nodebb-themes)! NodeBB's theming system allows extention 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://www.transifex.com/projects/p/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.
@@ -48,13 +36,13 @@ Our minimalist "Persona" theme gets you going right away, no coding experience r
NodeBB requires the following software to be installed: NodeBB requires the following software to be installed:
* A version of Node.js at least 8 or greater ([installation/upgrade instructions](https://github.com/nodesource/distributions)) * A version of Node.js at least 0.10 or greater
* Redis, version 2.8.9 or greater **or** MongoDB, version 2.6 or greater * Redis, version 2.6 or greater **or** MongoDB, version 2.6 or greater
* 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](http://docs.nodebb.org/en/latest/installing/os.html)
## Securing NodeBB ## Securing NodeBB
@@ -69,10 +57,8 @@ It is important to ensure that your NodeBB and database servers are secured. Bea
## 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/en/latest/upgrading/index.html)
## License ## License
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.

372
app.js
View File

@@ -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-2017 NodeBB Inc. Copyright (C) 2013-2014 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
@@ -17,63 +17,345 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
'use strict'; "use strict";
/*global require, global, process*/
require('./require-main'); var nconf = require('nconf');
nconf.argv().env();
const nconf = require('nconf'); var fs = require('fs'),
nconf.argv().env({ os = require('os'),
separator: '__', semver = require('semver'),
}); winston = require('winston'),
path = require('path'),
cluster = require('cluster'),
pkg = require('./package.json'),
utils = require('./public/src/utils.js');
const winston = require('winston');
const path = require('path');
const file = require('./src/file');
global.env = process.env.NODE_ENV || 'production'; global.env = process.env.NODE_ENV || 'production';
// Alternate configuration file support winston.remove(winston.transports.Console);
const configFile = path.resolve(__dirname, nconf.any(['config', 'CONFIG']) || 'config.json'); winston.add(winston.transports.Console, {
colorize: true,
timestamp: true
});
const configExists = file.existsSync(configFile) || (nconf.get('url') && nconf.get('secret') && nconf.get('database')); winston.add(winston.transports.File, {
filename: 'logs/error.log',
level: 'error'
});
const prestart = require('./src/prestart'); // TODO: remove once https://github.com/flatiron/winston/issues/280 is fixed
prestart.loadConfig(configFile); winston.err = function (err) {
prestart.setupWinston(); winston.error(err.stack);
prestart.versionCheck(); };
winston.verbose('* using configuration stored in: %s', configFile);
if (!process.send) { if(os.platform() === 'linux') {
require('child_process').exec('/usr/bin/which convert', function(err, stdout, stderr) {
if(err || !stdout) {
winston.warn('Couldn\'t find convert. Did you install imagemagick?');
}
});
}
if (!cluster.isWorker) {
// If run using `node app`, log GNU copyright info along with server info // If run using `node app`, log GNU copyright info along with server info
winston.info('NodeBB v' + nconf.get('version') + ' Copyright (C) 2013-' + (new Date()).getFullYear() + ' NodeBB Inc.'); winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013-2014 NodeBB Inc.');
winston.info('This program comes with ABSOLUTELY NO WARRANTY.'); winston.info('This program comes with ABSOLUTELY NO WARRANTY.');
winston.info('This is free software, and you are welcome to redistribute it under certain conditions.'); winston.info('This is free software, and you are welcome to redistribute it under certain conditions.');
winston.info(''); winston.info('');
} }
if (nconf.get('setup') || nconf.get('install')) { // Alternate configuration file support
require('./src/cli/setup').setup(); var configFile = path.join(__dirname, '/config.json'),
} else if (!configExists) { configExists;
require('./install/web').install(nconf.get('port'));
} else if (nconf.get('upgrade')) { if (nconf.get('config')) {
require('./src/cli/upgrade').upgrade(true); configFile = path.resolve(__dirname, nconf.get('config'));
} else if (nconf.get('reset')) { }
require('./src/cli/reset').reset({ configExists = fs.existsSync(configFile);
theme: nconf.get('t'),
plugin: nconf.get('p'), if (!nconf.get('setup') && !nconf.get('install') && !nconf.get('upgrade') && !nconf.get('reset') && configExists) {
widgets: nconf.get('w'), start();
settings: nconf.get('s'), } else if (nconf.get('setup') || nconf.get('install') || !configExists) {
all: nconf.get('a'), setup();
}); } else if (nconf.get('upgrade')) {
} else if (nconf.get('activate')) { upgrade();
require('./src/cli/manage').activate(nconf.get('activate')); } else if (nconf.get('reset')) {
} else if (nconf.get('plugins')) { reset();
require('./src/cli/manage').listPlugins(); }
} else if (nconf.get('build')) {
require('./src/cli/manage').build(nconf.get('build')); function loadConfig() {
} else if (nconf.get('events')) { nconf.file({
require('./src/cli/manage').listEvents(); file: configFile
} else { });
require('./src/start').start();
nconf.defaults({
base_dir: __dirname,
themes_path: path.join(__dirname, 'node_modules'),
upload_url: '/uploads/',
views_dir: path.join(__dirname, 'public/templates')
});
// Ensure themes_path is a full filepath
nconf.set('themes_path', path.resolve(__dirname, nconf.get('themes_path')));
nconf.set('core_templates_path', path.join(__dirname, 'src/views'));
nconf.set('base_templates_path', path.join(nconf.get('themes_path'), 'nodebb-theme-vanilla/templates'));
}
function start() {
loadConfig();
if (!cluster.isWorker || process.env.cluster_setup === 'true') {
winston.info('Time: ' + new Date());
winston.info('Initializing NodeBB v' + pkg.version);
winston.info('* using configuration stored in: ' + configFile);
}
if (cluster.isWorker && process.env.cluster_setup === 'true') {
var host = nconf.get(nconf.get('database') + ':host'),
storeLocation = host ? 'at ' + host + (host.indexOf('/') === -1 ? ':' + nconf.get(nconf.get('database') + ':port') : '') : '';
winston.info('* using ' + nconf.get('database') +' store ' + storeLocation);
winston.info('* using themes stored in: ' + nconf.get('themes_path'));
}
require('./src/database').init(function(err) {
if (err) {
winston.error(err.stack);
process.exit();
}
var meta = require('./src/meta');
meta.configs.init(function () {
var templates = require('templates.js'),
webserver = require('./src/webserver'),
sockets = require('./src/socket.io'),
plugins = require('./src/plugins'),
upgrade = require('./src/upgrade');
templates.setGlobal('relative_path', nconf.get('relative_path'));
upgrade.check(function(schema_ok) {
if (schema_ok || nconf.get('check-schema') === false) {
sockets.init(webserver.server);
nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path'));
plugins.ready(function() {
webserver.init(function() {
webserver.listen(function() {
process.send({
action: 'ready'
});
});
});
});
process.on('SIGTERM', shutdown);
process.on('SIGINT', shutdown);
process.on('SIGHUP', restart);
process.on('message', function(message) {
switch(message.action) {
case 'reload':
meta.reload();
break;
case 'js-propagate':
meta.js.cache = message.cache;
meta.js.map = message.map;
winston.info('[cluster] Client-side javascript and mapping propagated to worker ' + cluster.worker.id);
break;
case 'css-propagate':
meta.css.cache = message.cache;
meta.css.acpCache = message.acpCache;
winston.info('[cluster] Stylesheets propagated to worker ' + cluster.worker.id);
break;
}
});
process.on('uncaughtException', function(err) {
winston.error(err.stack);
console.log(err.stack);
meta.js.killMinifier();
shutdown(1);
});
} else {
winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:');
winston.warn(' ./nodebb upgrade');
if (cluster.isWorker) {
cluster.worker.kill();
} else {
process.exit();
}
}
});
});
});
}
function setup() {
loadConfig();
if (nconf.get('setup')) {
winston.info('NodeBB Setup Triggered via Command Line');
} else {
winston.warn('Configuration not found, starting NodeBB setup');
}
var install = require('./src/install');
winston.info('Welcome to NodeBB!');
winston.info('This looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.');
winston.info('Press enter to accept the default setting (shown in brackets).');
install.setup(function (err) {
if (err) {
winston.error('There was a problem completing NodeBB setup: ', err.message);
} else {
winston.info('NodeBB Setup Completed. Run \'./nodebb start\' to manually start your NodeBB server.');
}
process.exit();
});
}
function upgrade() {
loadConfig();
require('./src/database').init(function(err) {
if (err) {
winston.error(err.stack);
process.exit();
}
require('./src/meta').configs.init(function () {
require('./src/upgrade').upgrade();
});
});
}
function reset() {
loadConfig();
require('./src/database').init(function(err) {
if (err) {
winston.error(err.message);
process.exit();
}
if (nconf.get('themes')) {
resetThemes();
} else if (nconf.get('plugin')) {
resetPlugin(nconf.get('plugin'));
} else if (nconf.get('plugins')) {
resetPlugins();
} else if (nconf.get('widgets')) {
resetWidgets();
} else if (nconf.get('settings')) {
resetSettings();
} else if (nconf.get('all')) {
require('async').series([resetWidgets, resetThemes, resetPlugins, resetSettings], function(err) {
if (!err) {
winston.info('[reset] Reset complete.');
} else {
winston.error('[reset] Errors were encountered while resetting your forum settings: ' + err.message);
}
process.exit();
});
} else {
winston.warn('[reset] Nothing reset.');
}
});
}
function resetSettings(callback) {
var meta = require('./src/meta');
meta.configs.set('allowLocalLogin', 1, function(err) {
winston.info('[reset] Settings reset to default');
if (typeof callback === 'function') {
callback(err);
} else {
process.exit();
}
});
}
function resetThemes(callback) {
var meta = require('./src/meta');
meta.themes.set({
type: 'local',
id: 'nodebb-theme-vanilla'
}, function(err) {
winston.info('[reset] Theme reset to Vanilla');
if (typeof callback === 'function') {
callback(err);
} else {
process.exit();
}
});
}
function resetPlugin(pluginId) {
var db = require('./src/database');
db.setRemove('plugins:active', pluginId, function(err, result) {
if (err || result !== 1) {
winston.error('[reset] Could not disable plugin: ' + pluginId);
if (err) {
winston.error('[reset] Encountered error: ' + err.message);
} else {
winston.info('[reset] Perhaps it has already been disabled?');
}
} else {
winston.info('[reset] Plugin `' + pluginId + '` disabled');
}
process.exit();
});
}
function resetPlugins(callback) {
var db = require('./src/database');
db.delete('plugins:active', function(err) {
winston.info('[reset] All Plugins De-activated');
if (typeof callback === 'function') {
callback(err);
} else {
process.exit();
}
});
}
function resetWidgets(callback) {
require('./src/widgets').reset(function(err) {
winston.info('[reset] All Widgets moved to Draft Zone');
if (typeof callback === 'function') {
callback(err);
} else {
process.exit();
}
});
}
function shutdown(code) {
winston.info('[app] Shutdown (SIGTERM/SIGINT) Initialised.');
require('./src/database').close();
winston.info('[app] Database connection closed.');
require('./src/webserver').server.close();
winston.info('[app] Web server closed to connections.');
winston.info('[app] Shutdown complete.');
process.exit(code || 0);
}
function restart() {
if (process.send) {
winston.info('[app] Restarting...');
process.send({
action: 'restart'
});
} else {
winston.error('[app] Could not restart server. Shutting down.');
shutdown(1);
}
} }

29
bcrypt.js Normal file
View File

@@ -0,0 +1,29 @@
'use strict';
var bcrypt = require('bcryptjs'),
async = require('async'),
action = process.argv[2];
switch(action) {
case 'compare':
bcrypt.compare(process.argv[3], process.argv[4], function(err, res) {
process.stdout.write(res ? 'true' : 'false');
});
break;
case 'hash':
async.waterfall([
async.apply(bcrypt.genSalt, parseInt(process.argv[3], 10)),
function(salt, next) {
bcrypt.hash(process.argv[4], salt, next);
}
], function(err, hash) {
if (!err) {
process.stdout.write(hash);
} else {
process.stderr.write(err.message);
}
});
break;
}

4
build/.gitignore vendored
View File

@@ -1,4 +0,0 @@
*
*/
!export
!.gitignore

View File

@@ -1,3 +0,0 @@
.
!.gitignore
!README

View File

@@ -1,5 +0,0 @@
This directory contains archives of user uploads that are prepared on-demand
when a user wants to retrieve a copy of their uploaded content.
You can delete the files in here at will. They will just be regenerated if
requested again.

View File

@@ -1,3 +0,0 @@
'use strict';
module.exports = { extends: ['@commitlint/config-angular'] };

View File

@@ -1,24 +0,0 @@
version: '3.5'
services:
node:
build: .
restart: unless-stopped
depends_on:
- db
expose:
- 4567 # use a reverse proxy like Traefik
db:
image: mongo:bionic
restart: unless-stopped
expose:
- 27017
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: root
volumes:
- mongo:/data/db
volumes:
mongo:

View File

@@ -2,7 +2,6 @@
{ {
"name": "Announcements", "name": "Announcements",
"description": "Announcements regarding our community", "description": "Announcements regarding our community",
"descriptionParsed": "<p>Announcements regarding our community</p>\n",
"bgColor": "#fda34b", "bgColor": "#fda34b",
"color": "#fff", "color": "#fff",
"icon" : "fa-bullhorn", "icon" : "fa-bullhorn",
@@ -10,8 +9,7 @@
}, },
{ {
"name": "General Discussion", "name": "General Discussion",
"description": "A place to talk about whatever you want", "description": "A place to talk about whateeeever you want",
"descriptionParsed": "<p>A place to talk about whatever you want</p>\n",
"bgColor": "#59b3d0", "bgColor": "#59b3d0",
"color": "#fff", "color": "#fff",
"icon" : "fa-comments-o", "icon" : "fa-comments-o",
@@ -20,7 +18,6 @@
{ {
"name": "Blogs", "name": "Blogs",
"description": "Blog posts from individual members", "description": "Blog posts from individual members",
"descriptionParsed": "<p>Blog posts from individual members</p>\n",
"bgColor": "#86ba4b", "bgColor": "#86ba4b",
"color": "#fff", "color": "#fff",
"icon" : "fa-newspaper-o", "icon" : "fa-newspaper-o",
@@ -29,7 +26,6 @@
{ {
"name": "Comments & Feedback", "name": "Comments & Feedback",
"description": "Got a question? Ask away!", "description": "Got a question? Ask away!",
"descriptionParsed": "<p>Got a question? Ask away!</p>\n",
"bgColor": "#e95c5a", "bgColor": "#e95c5a",
"color": "#fff", "color": "#fff",
"icon" : "fa-question", "icon" : "fa-question",

View File

@@ -1,132 +1,86 @@
{ [
"title": "NodeBB", {
"showSiteTitle": 1, "field": "title",
"defaultLang": "en-GB", "value": "NodeBB"
"loginDays": 14, },
"loginSeconds": 0, {
"loginAttempts": 5, "field": "showSiteTitle",
"lockoutDuration": 60, "value": "1"
"adminReloginDuration": 60, },
"postDelay": 10, {
"initialPostDelay": 10, "field": "postDelay",
"newbiePostDelay": 120, "value": 10
"postEditDuration": 0, },
"newbiePostEditDuration": 3600, {
"postDeleteDuration": 0, "field": "initialPostDelay",
"enablePostHistory": 1, "value": 10
"postCacheSize": 10485760, },
"disableChat": 0, {
"chatEditDuration": 0, "field": "minimumPostLength",
"chatDeleteDuration": 0, "value": 8
"chatMessageDelay": 200, },
"newbiePostDelayThreshold": 3, {
"postQueueReputationThreshold": 0, "field": "allowGuestSearching",
"groupsExemptFromPostQueue": ["administrators", "Global Moderators"], "value": 0
"minimumPostLength": 8, },
"maximumPostLength": 32767, {
"minimumTagsPerTopic": 0, "field": "allowTopicsThumbnail",
"maximumTagsPerTopic": 5, "value": 0
"minimumTagLength": 3, },
"maximumTagLength": 15, {
"allowTopicsThumbnail": 0, "field": "allowRegistration",
"registrationType": "normal", "value": 1
"registrationApprovalType": "normal", },
"allowAccountDelete": 1, {
"allowFileUploads": 0, "field": "allowLocalLogin",
"privateUploads": 0, "value": 1
"allowedFileExtensions": "png,jpg,bmp", },
"allowUserHomePage": 1, {
"allowMultipleBadges": 0, "field": "allowAccountDelete",
"maximumFileSize": 2048, "value": 1
"stripEXIFData": 1, },
"resizeImageWidthThreshold": 2000, {
"resizeImageWidth": 760, "field": "allowFileUploads",
"rejectImageWidth": 5000, "value": 0
"rejectImageHeight": 5000, },
"resizeImageQuality": 80, {
"topicThumbSize": 120, "field": "maximumFileSize",
"minimumTitleLength": 3, "value": 2048
"maximumTitleLength": 255, },
"minimumUsernameLength": 2, {
"maximumUsernameLength": 16, "field": "minimumTitleLength",
"minimumPasswordLength": 6, "value": 3
"minimumPasswordStrength": 1, },
"maximumSignatureLength": 255, {
"maximumAboutMeLength": 1000, "field": "maximumTitleLength",
"maximumUsersInChatRoom": 0, "value": 255
"maximumChatMessageLength": 1000, },
"maximumProfileImageSize": 256, {
"maximumCoverImageSize": 2048, "field": "minimumUsernameLength",
"profileImageDimension": 200, "value": 2
"profile:convertProfileImageToPNG": 0, },
"profile:keepAllUserImages": 0, {
"requireEmailConfirmation": 0, "field": "maximumUsernameLength",
"gdpr_enabled": 1, "value": 16
"allowProfileImageUploads": 1, },
"teaserPost": "last-reply", {
"allowPrivateGroups": 1, "field": "minimumPasswordLength",
"unreadCutoff": 2, "value": 6
"bookmarkThreshold": 5, },
"autoDetectLang": 1, {
"reputation:disabled": 0, "field": "maximumSignatureLength",
"downvote:disabled": 0, "value": 255
"disableSignatures": 0, },
"min:rep:downvote": 0, {
"min:rep:flag": 0, "field": "maximumProfileImageSize",
"min:rep:profile-picture": 0, "value": 256
"min:rep:cover-picture": 0, },
"min:rep:website": 0, {
"min:rep:aboutme": 0, "field": "profileImageDimension",
"min:rep:signature": 0, "value": 128
"notificationType_upvote": "notification", },
"notificationType_new-topic": "notification", {
"notificationType_new-reply": "notification", "field": "requireEmailConfirmation",
"notificationType_follow": "notification", "value": 0
"notificationType_new-chat": "notification", }
"notificationType_group-invite": "notification", ]
"notificationType_mention": "notification",
"notificationType_new-register": "notification",
"notificationType_post-queue": "notification",
"notificationType_new-post-flag": "notification",
"notificationType_new-user-flag": "notification",
"topicStaleDays": 60,
"maxTopicsPerPage": 20,
"maxPostsPerPage": 20,
"topicsPerPage": 20,
"postsPerPage": 20,
"userSearchResultsPerPage": 50,
"maximumGroupNameLength": 255,
"maximumGroupTitleLength": 40,
"preventTopicDeleteAfterReplies": 0,
"feeds:disableSitemap": 0,
"sitemapTopics": 500,
"maintenanceMode": 0,
"maintenanceModeStatus": 503,
"votesArePublic": 0,
"maximumInvites": 0,
"username:disableEdit": 0,
"email:disableEdit": 0,
"email:sendmail:rateLimit": 2,
"email:sendmail:rateDelta": 1000,
"hideFullname": 0,
"allowGuestHandles": 0,
"disableRecentCategoryFilter": 0,
"maximumRelatedTopics": 0,
"disableEmailSubscriptions": 0,
"emailConfirmInterval": 10,
"inviteExpiration": 7,
"digestHour": 17,
"passwordExpiryDays": 0,
"hsts-maxage": 31536000,
"hsts-subdomains": 0,
"hsts-preload": 0,
"hsts-enabled": 0,
"eventLoopCheckEnabled": 1,
"eventLoopLagThreshold": 100,
"eventLoopInterval": 500,
"onlineCutoff": 30,
"timeagoCutoff": 30,
"necroThreshold": 7,
"categoryWatchState": "watching",
"submitPluginUsage": 1
}

View File

@@ -1,10 +0,0 @@
[
{
"widget": "html",
"data" : {
"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":"",
"container":""
}
}
]

View File

@@ -1,72 +0,0 @@
[
{
"route": "/categories",
"title": "[[global:header.categories]]",
"enabled": true,
"iconClass": "fa-list",
"textClass": "visible-xs-inline",
"text": "[[global:header.categories]]"
},
{
"id": "unread-count",
"route": "/unread",
"title": "[[global:header.unread]]",
"enabled": true,
"iconClass": "fa-inbox",
"textClass": "visible-xs-inline",
"text": "[[global:header.unread]]",
"groups": ["registered-users"]
},
{
"route": "/recent",
"title": "[[global:header.recent]]",
"enabled": true,
"iconClass": "fa-clock-o",
"textClass": "visible-xs-inline",
"text": "[[global:header.recent]]"
},
{
"route": "/tags",
"title": "[[global:header.tags]]",
"enabled": true,
"iconClass": "fa-tags",
"textClass": "visible-xs-inline",
"text": "[[global:header.tags]]"
},
{
"route": "/popular",
"title": "[[global:header.popular]]",
"enabled": true,
"iconClass": "fa-fire",
"textClass": "visible-xs-inline",
"text": "[[global:header.popular]]"
},
{
"route": "/users",
"title": "[[global:header.users]]",
"enabled": true,
"iconClass": "fa-user",
"textClass": "visible-xs-inline",
"text": "[[global:header.users]]"
},
{
"route": "/groups",
"title": "[[global:header.groups]]",
"enabled": true,
"iconClass": "fa-group",
"textClass": "visible-xs-inline",
"text": "[[global:header.groups]]"
},
{
"route": "/admin",
"title": "[[global:header.admin]]",
"enabled": true,
"iconClass": "fa-cogs",
"textClass": "visible-xs-inline",
"text": "[[global:header.admin]]",
"groups": ["administrators"],
"properties": {
"targetBlank": false
}
}
]

View File

@@ -1,10 +0,0 @@
# 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.
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
* [NodeBB Documentation](https://docs.nodebb.org)
* [Community Support Forum](https://community.nodebb.org)
* [Project repository](https://github.com/nodebb/nodebb)

View File

@@ -1,98 +1,104 @@
'use strict'; "use strict";
var async = require('async'); var async = require('async'),
var prompt = require('prompt'); prompt = require('prompt'),
var winston = require('winston'); nconf = require('nconf'),
winston = require('winston'),
var questions = { questions = {};
redis: require('../src/database/redis').questions,
mongo: require('../src/database/mongo').questions,
postgres: require('../src/database/postgres').questions,
};
module.exports = function (config, callback) { function success(err, config, callback) {
async.waterfall([
function (next) {
winston.info('\nNow configuring ' + config.database + ' database:');
getDatabaseConfig(config, next);
},
function (databaseConfig, next) {
saveDatabaseConfig(config, databaseConfig, next);
},
], callback);
};
function getDatabaseConfig(config, callback) {
if (!config) { if (!config) {
return callback(new Error('aborted')); return callback(new Error('aborted'));
} }
if (config.database === 'redis') { var database = (config.redis || config.mongo) ? config.secondary_database : config.database;
if (config['redis:host'] && config['redis:port']) {
callback(null, config);
} else {
prompt.get(questions.redis, callback);
}
} else if (config.database === 'mongo') {
if ((config['mongo:host'] && config['mongo:port']) || config['mongo:uri']) {
callback(null, config);
} else {
prompt.get(questions.mongo, callback);
}
} else if (config.database === 'postgres') {
if (config['postgres:host'] && config['postgres:port']) {
callback(null, config);
} else {
prompt.get(questions.postgres, callback);
}
} else {
return callback(new Error('unknown database : ' + config.database));
}
}
function saveDatabaseConfig(config, databaseConfig, callback) { function dbQuestionsSuccess(err, databaseConfig) {
if (!databaseConfig) { if (!databaseConfig) {
return callback(new Error('aborted')); return callback(new Error('aborted'));
} }
// Translate redis properties into redis object // Translate redis properties into redis object
if (config.database === 'redis') { if(database === 'redis') {
config.redis = { config.redis = {
host: databaseConfig['redis:host'], host: databaseConfig['redis:host'],
port: databaseConfig['redis:port'], port: databaseConfig['redis:port'],
password: databaseConfig['redis:password'], password: databaseConfig['redis:password'],
database: databaseConfig['redis:database'], database: databaseConfig['redis:database']
}; };
if (config.redis.host.slice(0, 1) === '/') { if (config.redis.host.slice(0, 1) === '/') {
delete config.redis.port; delete config.redis.port;
} }
} else if (config.database === 'mongo') { } else if (database === 'mongo') {
config.mongo = { config.mongo = {
host: databaseConfig['mongo:host'], host: databaseConfig['mongo:host'],
port: databaseConfig['mongo:port'], port: databaseConfig['mongo:port'],
username: databaseConfig['mongo:username'], username: databaseConfig['mongo:username'],
password: databaseConfig['mongo:password'], password: databaseConfig['mongo:password'],
database: databaseConfig['mongo:database'], database: databaseConfig['mongo:database']
uri: databaseConfig['mongo:uri'],
};
} else if (config.database === 'postgres') {
config.postgres = {
host: databaseConfig['postgres:host'],
port: databaseConfig['postgres:port'],
username: databaseConfig['postgres:username'],
password: databaseConfig['postgres:password'],
database: databaseConfig['postgres:database'],
ssl: databaseConfig['postgres:ssl'],
}; };
} else { } else {
return callback(new Error('unknown database : ' + config.database)); return callback(new Error('unknown database : ' + database));
} }
var allQuestions = questions.redis.concat(questions.mongo).concat(questions.postgres); var allQuestions = questions.redis.concat(questions.mongo);
for (var x = 0; x < allQuestions.length; x += 1) { for(var x=0;x<allQuestions.length;x++) {
delete config[allQuestions[x].name]; delete config[allQuestions[x].name];
} }
callback(null, config); callback(err, config);
}
if(database === 'redis') {
if (config['redis:host'] && config['redis:port']) {
dbQuestionsSuccess(null, config);
} else {
prompt.get(questions.redis, dbQuestionsSuccess);
}
} else if(database === 'mongo') {
if (config['mongo:host'] && config['mongo:port']) {
dbQuestionsSuccess(null, config);
} else {
prompt.get(questions.mongo, dbQuestionsSuccess);
}
} else {
return callback(new Error('unknown database : ' + database));
}
} }
function getSecondaryDatabaseModules(config, next) {
prompt.get({
"name": "secondary_db_modules",
"description": "Which database modules should " + config.secondary_database + " store?",
"default": nconf.get('secondary_db_modules') || "hash, list, sets, sorted"
}, function(err, db) {
config.secondary_db_modules = db.secondary_db_modules;
success(err, config, next);
});
}
module.exports = function(err, config, databases, callback) {
var allowedDBs = Object.keys(databases);
allowedDBs.forEach(function(db) {
questions[db] = require('./../src/database/' + db).questions;
});
async.waterfall([
function(next) {
process.stdout.write('\n');
winston.info('Now configuring ' + config.database + ' database:');
success(err, config, next);
},
function(config, next) {
if (config.secondary_database && allowedDBs.indexOf(config.secondary_database) !== -1) {
winston.info('Now configuring ' + config.secondary_database + ' database:');
getSecondaryDatabaseModules(config, next);
} else {
next(err, config);
}
}
], callback);
};

View File

@@ -1,173 +0,0 @@
{
"name": "nodebb",
"license": "GPL-3.0",
"description": "NodeBB Forum",
"version": "1.13.0",
"homepage": "http://www.nodebb.org",
"repository": {
"type": "git",
"url": "https://github.com/NodeBB/NodeBB/"
},
"main": "app.js",
"scripts": {
"start": "node loader.js",
"lint": "eslint --cache ./nodebb .",
"pretest": "npm run lint",
"test": "nyc --reporter=html --reporter=text-summary mocha",
"coveralls": "nyc report --reporter=text-lcov | coveralls && rm -r coverage"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
},
"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
]
},
"dependencies": {
"ace-builds": "^1.2.9",
"archiver": "^3.0.0",
"async": "^3.0.1",
"autoprefixer": "^9.4.6",
"bcryptjs": "2.4.3",
"benchpressjs": "^2.0.0",
"body-parser": "^1.18.2",
"bootstrap": "^3.4.0",
"bootswatch": "git://github.com/thomaspark/bootswatch.git#c41a8f066feb8950c6f9c6bcf5a3c37d1085404e",
"chart.js": "^2.7.1",
"cli-graph": "^3.2.2",
"clipboard": "^2.0.1",
"colors": "^1.1.2",
"commander": "^3.0.0",
"compression": "^1.7.1",
"connect-ensure-login": "^0.1.1",
"connect-flash": "^0.1.1",
"connect-mongo": "3.2.0",
"connect-multiparty": "^2.1.0",
"connect-pg-simple": "^6.0.0",
"connect-redis": "4.0.3",
"cookie-parser": "^1.4.3",
"cron": "^1.3.0",
"cropperjs": "^1.2.2",
"csurf": "^1.9.0",
"daemon": "^1.1.0",
"diff": "^4.0.1",
"express": "^4.16.2",
"express-session": "^1.15.6",
"express-useragent": "^1.0.12",
"graceful-fs": "^4.1.11",
"helmet": "^3.11.0",
"html-to-text": "^5.0.0",
"ipaddr.js": "^1.5.4",
"jquery": "^3.2.1",
"jsesc": "2.5.2",
"json-2-csv": "^3.0.0",
"jsonwebtoken": "^8.4.0",
"less": "^3.10.3",
"lodash": "^4.17.15",
"logrotate-stream": "^0.2.5",
"lru-cache": "5.1.1",
"material-design-lite": "^1.3.0",
"mime": "^2.2.0",
"mkdirp": "^0.5.1",
"mongodb": "3.4.0",
"morgan": "^1.9.1",
"mousetrap": "^1.6.1",
"mubsub-nbb": "^1.5.1",
"nconf": "^0.10.0",
"nodebb-plugin-composer-default": "6.3.20",
"nodebb-plugin-dbsearch": "4.0.7",
"nodebb-plugin-emoji": "^3.0.0",
"nodebb-plugin-emoji-android": "2.0.0",
"nodebb-plugin-markdown": "8.11.0",
"nodebb-plugin-mentions": "2.7.3",
"nodebb-plugin-soundpack-default": "1.0.0",
"nodebb-plugin-spam-be-gone": "0.6.7",
"nodebb-rewards-essentials": "0.1.2",
"nodebb-theme-lavender": "5.0.11",
"nodebb-theme-persona": "10.1.30",
"nodebb-theme-slick": "1.2.28",
"nodebb-theme-vanilla": "11.1.12",
"nodebb-widget-essentials": "4.0.17",
"nodemailer": "^6.0.0",
"passport": "^0.4.0",
"passport-local": "1.0.0",
"pg": "^7.4.0",
"pg-cursor": "^2.0.0",
"postcss": "7.0.21",
"postcss-clean": "1.1.0",
"promise-polyfill": "^8.0.0",
"prompt": "^1.0.0",
"redis": "2.8.0",
"request": "2.88.0",
"rimraf": "3.0.0",
"rss": "^1.2.2",
"sanitize-html": "^1.16.3",
"semver": "^7.0.0",
"serve-favicon": "^2.4.5",
"sharp": "0.23.4",
"sitemap": "^5.0.0",
"socket.io": "2.3.0",
"socket.io-adapter-cluster": "^1.0.1",
"socket.io-adapter-mongo": "^2.0.4",
"socket.io-adapter-postgres": "^1.2.1",
"socket.io-client": "2.3.0",
"socket.io-redis": "5.2.0",
"socketio-wildcard": "2.0.0",
"spdx-license-list": "^6.0.0",
"spider-detector": "2.0.0",
"textcomplete": "^0.17.1",
"textcomplete.contenteditable": "^0.1.1",
"toobusy-js": "^0.5.1",
"uglify-es": "^3.3.9",
"validator": "12.1.0",
"winston": "3.2.1",
"xml": "^1.0.1",
"xregexp": "^4.1.1",
"zxcvbn": "^4.4.2"
},
"devDependencies": {
"@commitlint/cli": "8.2.0",
"@commitlint/config-angular": "8.2.0",
"coveralls": "3.0.9",
"eslint": "6.7.0",
"eslint-config-airbnb-base": "14.0.0",
"eslint-plugin-import": "2.18.2",
"grunt": "1.0.4",
"grunt-contrib-watch": "1.1.0",
"husky": "3.1.0",
"jsdom": "15.2.1",
"lint-staged": "9.4.2",
"mocha": "6.2.2",
"mocha-lcov-reporter": "1.3.0",
"nyc": "14.1.1",
"smtp-server": "3.5.0"
},
"bugs": {
"url": "https://github.com/NodeBB/NodeBB/issues"
},
"engines": {
"node": ">=8"
},
"maintainers": [
{
"name": "Andrew Rodrigues",
"email": "andrew@nodebb.org",
"url": "https://github.com/psychobunny"
},
{
"name": "Julian Lam",
"email": "julian@nodebb.org",
"url": "https://github.com/julianlam"
},
{
"name": "Barış Soner Uşaklı",
"email": "baris@nodebb.org",
"url": "https://github.com/barisusakli"
}
]
}

View File

@@ -1,296 +0,0 @@
'use strict';
var winston = require('winston');
var express = require('express');
var bodyParser = require('body-parser');
var fs = require('fs');
var path = require('path');
var childProcess = require('child_process');
var less = require('less');
var async = require('async');
var uglify = require('uglify-es');
var nconf = require('nconf');
var Benchpress = require('benchpressjs');
var app = express();
var server;
var formats = [
winston.format.colorize(),
];
const timestampFormat = winston.format((info) => {
var dateString = new Date().toISOString() + ' [' + global.process.pid + ']';
info.level = dateString + ' - ' + info.level;
return info;
});
formats.push(timestampFormat());
formats.push(winston.format.splat());
formats.push(winston.format.simple());
winston.configure({
level: 'verbose',
format: winston.format.combine.apply(null, formats),
transports: [
new winston.transports.Console({
handleExceptions: true,
}),
new winston.transports.File({
filename: 'logs/webinstall.log',
handleExceptions: true,
}),
],
});
var web = module.exports;
var scripts = [
'node_modules/jquery/dist/jquery.js',
'public/vendor/xregexp/xregexp.js',
'public/vendor/xregexp/unicode/unicode-base.js',
'public/src/utils.js',
'public/src/installer/install.js',
'node_modules/zxcvbn/dist/zxcvbn.js',
];
var installing = false;
var success = false;
var error = false;
var launchUrl;
web.install = function (port) {
port = port || 4567;
winston.info('Launching web installer on port ' + port);
app.use(express.static('public', {}));
app.engine('tpl', function (filepath, options, callback) {
async.waterfall([
function (next) {
fs.readFile(filepath, 'utf-8', next);
},
function (buffer, next) {
Benchpress.compileParse(buffer.toString(), options, next);
},
], callback);
});
app.set('view engine', 'tpl');
app.set('views', path.join(__dirname, '../src/views'));
app.use(bodyParser.urlencoded({
extended: true,
}));
async.parallel([compileLess, compileJS, copyCSS, loadDefaults], function (err) {
if (err) {
winston.error(err);
}
setupRoutes();
launchExpress(port);
});
};
function launchExpress(port) {
server = app.listen(port, function () {
winston.info('Web installer listening on http://%s:%s', '0.0.0.0', port);
});
}
function setupRoutes() {
app.get('/', welcome);
app.post('/', install);
app.post('/launch', launch);
app.get('/ping', ping);
app.get('/sping', ping);
}
function ping(req, res) {
res.status(200).send(req.path === '/sping' ? 'healthy' : '200');
}
function welcome(req, res) {
var dbs = ['redis', 'mongo', 'postgres'];
var databases = dbs.map(function (databaseName) {
var questions = require('../src/database/' + databaseName).questions.filter(function (question) {
return question && !question.hideOnWebInstall;
});
return {
name: databaseName,
questions: questions,
};
});
var defaults = require('./data/defaults');
res.render('install/index', {
url: nconf.get('url') || (req.protocol + '://' + req.get('host')),
launchUrl: launchUrl,
skipGeneralSetup: !!nconf.get('url'),
databases: databases,
skipDatabaseSetup: !!nconf.get('database'),
error: error,
success: success,
values: req.body,
minimumPasswordLength: defaults.minimumPasswordLength,
minimumPasswordStrength: defaults.minimumPasswordStrength,
installing: installing,
});
}
function install(req, res) {
if (installing) {
return welcome(req, res);
}
req.setTimeout(0);
installing = true;
var setupEnvVars = nconf.get();
for (var i in req.body) {
if (req.body.hasOwnProperty(i) && !process.env.hasOwnProperty(i)) {
setupEnvVars[i.replace(':', '__')] = req.body[i];
}
}
// Flatten any objects in setupEnvVars
const pushToRoot = function (parentKey, key) {
setupEnvVars[parentKey + '__' + key] = setupEnvVars[parentKey][key];
};
for (var j in setupEnvVars) {
if (setupEnvVars.hasOwnProperty(j) && typeof setupEnvVars[j] === 'object' && setupEnvVars[j] !== null && !Array.isArray(setupEnvVars[j])) {
Object.keys(setupEnvVars[j]).forEach(pushToRoot.bind(null, j));
delete setupEnvVars[j];
} else if (Array.isArray(setupEnvVars[j])) {
setupEnvVars[j] = JSON.stringify(setupEnvVars[j]);
}
}
winston.info('Starting setup process');
winston.info(setupEnvVars);
launchUrl = setupEnvVars.url;
var child = require('child_process').fork('app', ['--setup'], {
env: setupEnvVars,
});
child.on('close', function (data) {
installing = false;
success = data === 0;
error = data !== 0;
welcome(req, res);
});
}
function launch(req, res) {
res.json({});
server.close();
req.setTimeout(0);
var child;
if (!nconf.get('launchCmd')) {
child = childProcess.spawn('node', ['loader.js'], {
detached: true,
stdio: ['ignore', 'ignore', 'ignore'],
});
console.log('\nStarting NodeBB');
console.log(' "./nodebb stop" to stop the NodeBB server');
console.log(' "./nodebb log" to view server output');
console.log(' "./nodebb restart" to restart NodeBB');
} else {
// Use launchCmd instead, if specified
child = childProcess.exec(nconf.get('launchCmd'), {
detached: true,
stdio: ['ignore', 'ignore', 'ignore'],
});
}
var filesToDelete = [
'installer.css',
'installer.min.js',
'bootstrap.min.css',
];
async.each(filesToDelete, function (filename, next) {
fs.unlink(path.join(__dirname, '../public', filename), next);
}, function (err) {
if (err) {
winston.warn('Unable to remove installer files');
}
child.unref();
process.exit(0);
});
}
function compileLess(callback) {
fs.readFile(path.join(__dirname, '../public/less/install.less'), function (err, style) {
if (err) {
return winston.error('Unable to read LESS install file: ', err);
}
less.render(style.toString(), function (err, css) {
if (err) {
return winston.error('Unable to compile LESS: ', err);
}
fs.writeFile(path.join(__dirname, '../public/installer.css'), css.css, callback);
});
});
}
function compileJS(callback) {
var code = '';
async.eachSeries(scripts, function (srcPath, next) {
fs.readFile(path.join(__dirname, '..', srcPath), function (err, buffer) {
if (err) {
return next(err);
}
code += buffer.toString();
next();
});
}, function (err) {
if (err) {
return callback(err);
}
try {
var minified = uglify.minify(code, {
compress: false,
});
if (!minified.code) {
return callback(new Error('[[error:failed-to-minify]]'));
}
fs.writeFile(path.join(__dirname, '../public/installer.min.js'), minified.code, callback);
} catch (e) {
callback(e);
}
});
}
function copyCSS(next) {
async.waterfall([
function (next) {
fs.readFile(path.join(__dirname, '../node_modules/bootstrap/dist/css/bootstrap.min.css'), 'utf8', next);
},
function (src, next) {
fs.writeFile(path.join(__dirname, '../public/bootstrap.min.css'), src, next);
},
], next);
}
function loadDefaults(next) {
var setupDefaultsPath = path.join(__dirname, '../setup.json');
fs.access(setupDefaultsPath, fs.constants.F_OK | fs.constants.R_OK, function (err) {
if (err) {
// setup.json not found or inaccessible, proceed with no defaults
return setImmediate(next);
}
winston.info('[installer] Found setup.json, populating default values');
nconf.file({
file: setupDefaultsPath,
});
next();
});
}

347
loader.js
View File

@@ -1,55 +1,50 @@
'use strict'; "use strict";
var nconf = require('nconf'); var nconf = require('nconf'),
var fs = require('fs'); fs = require('fs'),
var url = require('url'); path = require('path'),
var path = require('path'); cluster = require('cluster'),
var fork = require('child_process').fork; async = require('async'),
var async = require('async'); logrotate = require('logrotate-stream'),
var logrotate = require('logrotate-stream');
var mkdirp = require('mkdirp');
var file = require('./src/file'); pkg = require('./package.json'),
var pkg = require('./package.json');
var pathToConfig = path.resolve(__dirname, process.env.CONFIG || 'config.json'); pidFilePath = __dirname + '/pidfile',
output = logrotate({ file: __dirname + '/logs/output.log', size: '1m', keep: 3, compress: true }),
silent = process.env.NODE_ENV !== 'development' ? true : false,
numProcs,
nconf.argv().env().file({ Loader = {
file: pathToConfig,
});
var pidFilePath = path.join(__dirname, 'pidfile');
var outputLogFilePath = path.join(__dirname, nconf.get('logFile') || 'logs/output.log');
var logDir = path.dirname(outputLogFilePath);
if (!fs.existsSync(logDir)) {
mkdirp.sync(path.dirname(outputLogFilePath));
}
var output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true });
var silent = nconf.get('silent') === 'false' ? false : nconf.get('silent') !== false;
var numProcs;
var workers = [];
var Loader = {
timesStarted: 0, timesStarted: 0,
}; shutdown_queue: [],
var appPath = path.join(__dirname, 'app.js'); js: {
cache: undefined,
map: undefined
},
css: {
cache: undefined,
acpCache: undefined
}
};
Loader.init = function(callback) {
cluster.setupMaster({
exec: "app.js",
silent: silent
});
Loader.primaryWorker = 1;
Loader.init = function (callback) {
if (silent) { if (silent) {
console.log = function () { console.log = function(value) {
var args = Array.prototype.slice.call(arguments); output.write(value + '\n');
output.write(args.join(' ') + '\n');
}; };
} }
process.on('SIGHUP', Loader.restart); process.on('SIGHUP', Loader.restart);
process.on('SIGTERM', Loader.stop);
callback(); callback();
}; };
Loader.displayStartupMessages = function (callback) { Loader.displayStartupMessages = function(callback) {
console.log(''); console.log('');
console.log('NodeBB v' + pkg.version + ' Copyright (C) 2013-2014 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.');
@@ -59,61 +54,134 @@ Loader.displayStartupMessages = function (callback) {
callback(); callback();
}; };
Loader.addWorkerEvents = function (worker) { Loader.addClusterEvents = function(callback) {
worker.on('exit', function (code, signal) { cluster.on('fork', function(worker) {
worker.on('message', function(message) {
if (message && typeof message === 'object' && message.action) {
var otherWorkers;
switch (message.action) {
case 'ready':
if (Loader.js.cache) {
worker.send({
action: 'js-propagate',
cache: Loader.js.cache,
map: Loader.js.map
});
}
if (Loader.css.cache) {
worker.send({
action: 'css-propagate',
cache: Loader.css.cache,
acpCache: Loader.css.acpCache
});
}
// Kill an instance in the shutdown queue
var workerToKill = Loader.shutdown_queue.pop();
if (workerToKill) {
cluster.workers[workerToKill].kill();
}
break;
case 'restart':
console.log('[cluster] Restarting...');
Loader.restart(function(err) {
console.log('[cluster] Restarting...');
});
break;
case 'reload':
console.log('[cluster] Reloading...');
Loader.reload();
break;
case 'js-propagate':
Loader.js.cache = message.cache;
Loader.js.map = message.map;
otherWorkers = Object.keys(cluster.workers).filter(function(worker_id) {
return parseInt(worker_id, 10) !== parseInt(worker.id, 10);
});
otherWorkers.forEach(function(worker_id) {
cluster.workers[worker_id].send({
action: 'js-propagate',
cache: message.cache,
map: message.map
});
});
break;
case 'css-propagate':
Loader.css.cache = message.cache;
Loader.css.acpCache = message.acpCache;
otherWorkers = Object.keys(cluster.workers).filter(function(worker_id) {
return parseInt(worker_id, 10) !== parseInt(worker.id, 10);
});
otherWorkers.forEach(function(worker_id) {
cluster.workers[worker_id].send({
action: 'css-propagate',
cache: message.cache,
acpCache: message.acpCache
});
});
break;
case 'listening':
if (message.primary) {
Loader.primaryWorker = parseInt(worker.id, 10);
}
break;
case 'user:connect':
case 'user:disconnect':
case 'config:update':
Loader.notifyWorkers(message);
break;
}
}
});
});
cluster.on('listening', function(worker) {
console.log('[cluster] Child Process (' + worker.process.pid + ') listening for connections.');
});
cluster.on('exit', function(worker, code, signal) {
if (code !== 0) { if (code !== 0) {
if (Loader.timesStarted < numProcs * 3) { if (Loader.timesStarted < numProcs*3) {
Loader.timesStarted += 1; Loader.timesStarted++;
if (Loader.crashTimer) { if (Loader.crashTimer) {
clearTimeout(Loader.crashTimer); clearTimeout(Loader.crashTimer);
} }
Loader.crashTimer = setTimeout(function () { Loader.crashTimer = setTimeout(function() {
Loader.timesStarted = 0; Loader.timesStarted = 0;
}, 10000); });
} else { } else {
console.log((numProcs * 3) + ' restarts in 10 seconds, most likely an error on startup. Halting.'); console.log(numProcs*3 + ' restarts in 10 seconds, most likely an error on startup. Halting.');
process.exit(); process.exit();
} }
} }
console.log('[cluster] Child Process (' + worker.pid + ') has exited (code: ' + code + ', signal: ' + signal + ')'); console.log('[cluster] Child Process (' + worker.process.pid + ') has exited (code: ' + code + ', signal: ' + signal +')');
if (!(worker.suicide || code === 0)) { if (!worker.suicide) {
console.log('[cluster] Spinning up another process...'); console.log('[cluster] Spinning up another process...');
forkWorker(worker.index, worker.isPrimary); var wasPrimary = parseInt(worker.id, 10) === Loader.primaryWorker;
forkWorker(wasPrimary);
} }
}); });
worker.on('message', function (message) { cluster.on('disconnect', function(worker) {
if (message && typeof message === 'object' && message.action) { console.log('[cluster] Child Process (' + worker.process.pid + ') has disconnected');
switch (message.action) {
case 'restart':
console.log('[cluster] Restarting...');
Loader.restart();
break;
case 'pubsub':
workers.forEach(function (w) {
w.send(message);
});
break;
case 'socket.io':
workers.forEach(function (w) {
if (w !== worker) {
w.send(message);
}
});
break;
}
}
}); });
callback();
}; };
Loader.start = function (callback) { Loader.start = function(callback) {
numProcs = getPorts().length;
console.log('Clustering enabled: Spinning up ' + numProcs + ' process(es).\n'); console.log('Clustering enabled: Spinning up ' + numProcs + ' process(es).\n');
for (var x = 0; x < numProcs; x += 1) { for(var x=0; x<numProcs; ++x) {
forkWorker(x, x === 0); forkWorker(x === 0);
} }
if (callback) { if (callback) {
@@ -121,101 +189,49 @@ Loader.start = function (callback) {
} }
}; };
function forkWorker(index, isPrimary) { function forkWorker(isPrimary) {
var ports = getPorts(); var worker = cluster.fork({
var args = []; cluster_setup: isPrimary,
handle_jobs: isPrimary
if (!ports[index]) { }),
return console.log('[cluster] invalid port for worker : ' + index + ' ports: ' + ports.length); output = logrotate({ file: __dirname + '/logs/output.log', size: '1m', keep: 3, compress: true });
}
process.env.isPrimary = isPrimary;
process.env.isCluster = nconf.get('isCluster') || ports.length > 1;
process.env.port = ports[index];
var worker = fork(appPath, args, {
silent: silent,
env: process.env,
});
worker.index = index;
worker.isPrimary = isPrimary;
workers[index] = worker;
Loader.addWorkerEvents(worker);
if (silent) { if (silent) {
var output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true }); worker.process.stdout.pipe(output);
worker.stdout.pipe(output); worker.process.stderr.pipe(output);
worker.stderr.pipe(output);
} }
} }
function getPorts() { Loader.restart = function(callback) {
var _url = nconf.get('url'); // Slate existing workers for termination -- welcome to death row.
if (!_url) { Loader.shutdown_queue = Loader.shutdown_queue.concat(Object.keys(cluster.workers));
console.log('[cluster] url is undefined, please check your config.json');
process.exit();
}
var urlObject = url.parse(_url);
var port = nconf.get('PORT') || nconf.get('port') || urlObject.port || 4567;
if (!Array.isArray(port)) {
port = [port];
}
return port;
}
Loader.restart = function () {
killWorkers();
nconf.remove('file');
nconf.use('file', { file: pathToConfig });
fs.readFile(pathToConfig, { encoding: 'utf-8' }, function (err, configFile) {
if (err) {
console.error('Error reading config');
throw err;
}
var conf = JSON.parse(configFile);
nconf.stores.env.readOnly = false;
nconf.set('url', conf.url);
nconf.stores.env.readOnly = true;
if (process.env.url !== conf.url) {
process.env.url = conf.url;
}
Loader.start(); Loader.start();
};
Loader.reload = function() {
Object.keys(cluster.workers).forEach(function(worker_id) {
cluster.workers[worker_id].send({
action: 'reload'
});
}); });
}; };
Loader.stop = function () { Loader.notifyWorkers = function (msg) {
killWorkers(); Object.keys(cluster.workers).forEach(function(id) {
cluster.workers[id].send(msg);
// Clean up the pidfile });
if (nconf.get('daemon') !== 'false' && nconf.get('daemon') !== false) {
fs.unlinkSync(pidFilePath);
}
}; };
function killWorkers() {
workers.forEach(function (worker) {
worker.suicide = true;
worker.kill();
});
}
fs.open(pathToConfig, 'r', function (err) { nconf.argv().file({
if (err) { file: path.join(__dirname, '/config.json')
// No config detected, kickstart web installer });
fork('app');
return;
}
if (nconf.get('daemon') !== 'false' && nconf.get('daemon') !== false) { numProcs = nconf.get('cluster') || 1;
if (file.existsSync(pidFilePath)) { numProcs = (numProcs === true) ? require('os').cpus().length : numProcs;
if (nconf.get('daemon') !== false) {
if (fs.existsSync(pidFilePath)) {
try { try {
var pid = fs.readFileSync(pidFilePath, { encoding: 'utf-8' }); var pid = fs.readFileSync(pidFilePath, { encoding: 'utf-8' });
process.kill(pid, 0); process.kill(pid, 0);
@@ -225,23 +241,18 @@ fs.open(pathToConfig, 'r', function (err) {
} }
} }
require('daemon')({ require('daemon')();
stdout: process.stdout,
stderr: process.stderr,
cwd: process.cwd(),
});
fs.writeFileSync(pidFilePath, process.pid); fs.writeFile(__dirname + '/pidfile', process.pid);
} }
async.series([ async.series([
Loader.init, Loader.init,
Loader.displayStartupMessages, Loader.displayStartupMessages,
Loader.start, Loader.addClusterEvents,
], function (err) { Loader.start
], function(err) {
if (err) { if (err) {
console.error('[loader] Error during startup'); console.log('[loader] Error during startup: ' + err.message);
throw err;
} }
});
}); });

68
minifier.js Normal file
View File

@@ -0,0 +1,68 @@
"use strict";
var uglifyjs = require('uglify-js'),
less = require('less'),
async = require('async'),
fs = require('fs'),
path = require('path'),
crypto = require('crypto'),
Minifier = {
js: {}
};
/* Javascript */
Minifier.js.minify = function (scripts, relativePath, minify, callback) {
var options = {
compress: false,
sourceMapURL: '/nodebb.min.js.map',
outSourceMap: 'nodebb.min.js.map',
sourceRoot: relativePath
};
scripts = scripts.filter(function(file) {
return fs.existsSync(file);
});
if (!minify) {
options.mangle = false;
options.prefix = 1;
}
try {
var minified = uglifyjs.minify(scripts, options),
hasher = crypto.createHash('md5'),
hash;
// Calculate js hash
hasher.update(minified.code, 'utf-8');
hash = hasher.digest('hex');
process.send({
type: 'hash',
payload: hash.slice(0, 8)
});
callback({
js: minified.code,
map: minified.map
});
} catch(err) {
process.send({
type: 'error',
payload: err
});
}
};
process.on('message', function(payload) {
switch(payload.action) {
case 'js':
Minifier.js.minify(payload.scripts, payload.relativePath, payload.minify, function(data) {
process.send({
type: 'end',
data: data
});
});
break;
}
});

126
nodebb
View File

@@ -1,5 +1,125 @@
#!/usr/bin/env node #!/bin/bash
'use strict'; # $0 script path
# $1 action
# $2 subaction
require('./src/cli'); node="$(which nodejs 2>/dev/null)";
if [ $? -gt 0 ];
then node="$(which node)";
fi
function pidExists() {
if [ -e "pidfile" ];
then
kill -s 0 $(cat pidfile);
if [ !$? ];
then return 1;
else return 0;
fi
else
return 0;
fi
}
case "$1" in
start)
echo "Starting NodeBB";
echo " \"./nodebb stop\" to stop the NodeBB server";
echo " \"./nodebb log\" to view server output";
# Start the loader daemon
"$node" loader "$@"
;;
stop)
pidExists;
if [ 0 -eq $? ];
then
echo "NodeBB is already stopped.";
else
echo "Stopping NodeBB. Goodbye!";
kill $(cat pidfile);
fi
;;
reload|restart)
pidExists;
if [ 0 -eq $? ];
then
echo "NodeBB could not be restarted, as a running instance could not be found.";
else
echo "Restarting NodeBB.";
kill -1 $(cat pidfile);
fi
;;
status)
pidExists;
if [ 0 -eq $? ];
then
echo "NodeBB is not running";
echo " \"./nodebb start\" to launch the NodeBB server";
else
echo "NodeBB Running (pid $(cat pidfile))";
echo " \"./nodebb stop\" to stop the NodeBB server";
echo " \"./nodebb log\" to view server output";
echo " \"./nodebb restart\" to restart NodeBB";
fi
;;
log)
clear;
tail -F ./logs/output.log;
;;
upgrade)
npm install
# ls -d node_modules/nodebb* | xargs -n1 basename | xargs npm install
# ls -d node_modules/nodebb* | xargs -n1 basename | xargs npm update
npm i nodebb-theme-vanilla nodebb-theme-lavender nodebb-widget-essentials
"$node" app --upgrade
touch package.json
echo -e "\n\e[00;32mNodeBB Dependencies up-to-date!\e[00;00m";
;;
setup)
"$node" app --setup "$@"
;;
reset)
"$node" app --reset --$2
;;
dev)
echo "Launching NodeBB in \"development\" mode."
echo "To run the production build of NodeBB, please use \"forever\"."
echo "More Information: https://docs.nodebb.org/en/latest/running/index.html"
NODE_ENV=development "$node" loader --no-daemon "$@"
;;
watch)
echo "Launching NodeBB in \"development\" mode."
echo "To run the production build of NodeBB, please use \"forever\"."
echo "More Information: https://docs.nodebb.org/en/latest/running/index.html"
NODE_ENV=development supervisor -q --ignore public/templates,public/nodebb.min.js,public/nodebb.min.js.map --extensions 'node|js|tpl|less' -- app "$@"
;;
*)
echo "Welcome to NodeBB"
echo $"Usage: $0 {start|stop|reload|restart|log|setup|reset|upgrade|dev|watch}"
echo ''
column -s ' ' -t <<< '
start Start the NodeBB server
stop Stops the NodeBB server
reload Restarts NodeBB
restart Restarts NodeBB
log Opens the logging interface (useful for debugging)
setup Runs the NodeBB setup script
reset Disables all plugins, restores the default theme.
upgrade Run NodeBB upgrade scripts, ensure packages are up-to-date
dev Start NodeBB in interactive development mode
watch Start NodeBB in development mode and watch for changes
'
exit 1
esac

View File

@@ -1 +0,0 @@
@echo off && cd %~dp0 && node ./nodebb %*

2430
npm-shrinkwrap.json generated Normal file

File diff suppressed because it is too large Load Diff

92
package.json Normal file
View File

@@ -0,0 +1,92 @@
{
"name": "nodebb",
"license": "GPLv3 or later",
"description": "NodeBB Forum",
"version": "0.5.4",
"homepage": "http://www.nodebb.org",
"repository": {
"type": "git",
"url": "https://github.com/NodeBB/NodeBB/"
},
"main": "app.js",
"scripts": {
"start": "./nodebb start",
"stop": "./nodebb stop",
"test": "mocha ./tests -t 10000"
},
"dependencies": {
"async": "~0.9.0",
"bcryptjs": "~2.0.1",
"body-parser": "^1.9.0",
"compression": "^1.1.0",
"connect-ensure-login": "^0.1.1",
"connect-flash": "^0.1.1",
"connect-multiparty": "^1.2.4",
"cookie-parser": "^1.3.3",
"cron": "^1.0.5",
"csurf": "^1.6.1",
"daemon": "~1.1.0",
"express": "^4.9.5",
"express-session": "^1.8.2",
"gm": "1.16.0",
"gravatar": "^1.1.0",
"less": "^1.7.5",
"logrotate-stream": "^0.2.3",
"mkdirp": "~0.5.0",
"morgan": "^1.3.2",
"nconf": "~0.6.7",
"nodebb-plugin-dbsearch": "0.0.15",
"nodebb-plugin-markdown": "^0.7.0",
"nodebb-plugin-mentions": "~0.6.0",
"nodebb-plugin-soundpack-default": "~0.1.1",
"nodebb-theme-lavender": "~0.1.0",
"nodebb-theme-vanilla": "~0.1.0",
"nodebb-widget-essentials": "~0.1.1",
"npm": "^2.1.4",
"passport": "^0.2.1",
"passport-local": "1.0.0",
"prompt": "^0.2.14",
"request": "^2.44.0",
"rimraf": "~2.2.6",
"rss": "^1.0.0",
"semver": "^4.0.3",
"serve-favicon": "^2.1.5",
"sitemap": "^0.7.4",
"socket.io": "^0.9.17",
"socket.io-client": "^0.9.17",
"socket.io-wildcard": "~0.1.1",
"string": "^2.1.0",
"templates.js": "0.1.2",
"uglify-js": "git+https://github.com/julianlam/UglifyJS2.git",
"underscore": "~1.7.0",
"validator": "~3.21.0",
"winston": "^0.8.0",
"xregexp": "~2.0.0"
},
"devDependencies": {
"mocha": "~1.13.0"
},
"bugs": {
"url": "https://github.com/NodeBB/NodeBB/issues"
},
"engines": {
"node": ">=0.10"
},
"maintainers": [
{
"name": "Andrew Rodrigues",
"email": "andrew@designcreateplay.com",
"url": "https://github.com/psychobunny"
},
{
"name": "Julian Lam",
"email": "julian@designcreateplay.com",
"url": "https://github.com/julianlam"
},
{
"name": "Barış Soner Uşaklı",
"email": "baris@designcreateplay.com",
"url": "https://github.com/barisusakli"
}
]
}

View File

@@ -1,57 +0,0 @@
{
"globals": {
"app": true,
"io": true,
"socket": true,
"ajaxify": true,
"config": true,
"RELATIVE_PATH": true,
"utils": true,
"overrides": true,
"componentHandler": true,
"bootbox": true,
"Visibility": true,
"Tinycon": true,
"Promise": true
},
"env": {
"jquery": true,
"amd": true,
"browser": true,
"es6": false
},
"rules": {
"no-dupe-class-members": "off",
"no-var": "off",
"object-shorthand": "off",
"prefer-arrow-callback": "off",
"prefer-spread": "off",
"prefer-reflect": "off",
"prefer-template": "off"
},
"parserOptions": {
"ecmaVersion": 5,
"ecmaFeatures": {
"arrowFunctions": false,
"classes": false,
"defaultParams": false,
"destructuring": false,
"experimentalObjectRestSpread": false,
"blockBindings": false,
"forOf": false,
"generators": false,
"globalReturn": false,
"jsx": false,
"modules": false,
"objectLiteralComputedProperties": false,
"objectLiteralDuplicateProperties": false,
"objectLiteralShorthandMethods": false,
"objectLiteralShorthandProperties": false,
"impliedStrict": false,
"restParams": false,
"spread": false,
"superInFunctions": false,
"templateStrings": false
}
}
}

View File

@@ -1,84 +0,0 @@
{
"maxerr" : 50, // {int} Maximum error before stopping
// Enforcing
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase
"curly" : true, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
"indent" : 4, // {int} Number of spaces to use for indentation
"latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
"noempty" : true, // true: Prohibit use of empty blocks
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
"plusplus" : false, // true: Prohibit use of `++` & `--`
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : true, // true: Require all defined variables be used
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
"trailing" : false, // true: Prohibit trailing whitespaces
"maxparams" : false, // {int} Max number of formal params allowed per function
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
"maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
"maxlen" : false, // {int} Max number of characters per line
// Relaxing
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
"boss" : false, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
"eqnull" : false, // true: Tolerate use of `== null`
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
"esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// (ex: `for each`, multiple try/catch, function expression…)
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
"funcscope" : false, // true: Tolerate defining variables inside control statements"
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
"iterator" : false, // true: Tolerate using the `__iterator__` property
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
"laxcomma" : false, // true: Tolerate comma-first style coding
"loopfunc" : false, // true: Tolerate functions being defined in loops
"multistr" : false, // true: Tolerate multi-line strings
"proto" : false, // true: Tolerate using the `__proto__` property
"scripturl" : false, // true: Tolerate script-targeted URLs
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
"validthis" : false, // true: Tolerate using this in a non-constructor function
"globals": {
"app": true,
"io": true,
"socket": true,
"ajaxify": true,
"config": true,
"RELATIVE_PATH": true,
"utils": true,
"overrides": true,
"componentHandler": true,
"bootbox": true,
"templates": true,
"Visibility": true,
"Tinycon": true,
"require": true,
"define": true,
"ace": true,
"Sortable": true,
"Slideout": true,
"NProgress": true
},
"jquery": true,
"browser": true
}

View File

@@ -1,177 +0,0 @@
<html>
<head>
<title>Excessive Load Warning</title>
<link href='https://fonts.googleapis.com/css?family=Ubuntu:400,500,700' rel='stylesheet' type='text/css'>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style type="text/css">
body {
background: #00A9EA;
color: white;
font-family: 'Ubuntu', sans-serif;
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">
window.onload = function() {
var count = 0,
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">503</h1>
<p>
<strong>This forum is temporarily unavailable due to excessive load.</strong>
</p>
<p>
We shouldn't be down for long. Please check back shortly. Sorry for the inconvenience!
</p>
<p>
&nbsp;<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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -1,3 +0,0 @@
/*
The following stylesheet is only included on pages that can execute javascript
*/

View File

@@ -1,14 +0,0 @@
# Important note about localising NodeBB
The files here are read-only and overwritten daily (if there are changes) by the
helper bot [Misty](https://github.com/nodebb-misty).
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
localisations can go out-of-sync when edited directly.
If there are non-localised strings and you cannot find them in Transifex, please
[open a new issue on our bug tracker](https://github.com/NodeBB/NodeBB/issues/new)
so we can take a look.
Thank you for helping localise NodeBB!

3
public/language/TODO Normal file
View File

@@ -0,0 +1,3 @@
NPM INSTALL
For now, language packs will be stored here. Eventually, will be moved to server side to allow for npm install-ability.
When that happens, server code will generate compressed JSON language files in this folder.

View File

@@ -1,7 +0,0 @@
{
"alert.confirm-rebuild-and-restart": "هل أنت متأكد إنك تريد إعادة بناء وتشغيل الـ NodeBB؟",
"alert.confirm-restart": "هل تريد بالتأكيد إعادة تشغيل NodeBB؟",
"acp-title": "لوحة تحكم إدارة NodeBB | %1",
"settings-header-contents": "محتويات"
}

View File

@@ -1,11 +0,0 @@
{
"post-cache": "التخزين المؤقت للمشاركات",
"posts-in-cache": "المشاركات المخزنة مؤقتاً",
"average-post-size": "متوسط ​​حجم المشاركة",
"length-to-max": "الطول / أقصى حد",
"percent-full": "1% كاملة",
"post-cache-size": "حجم التخزين المؤقت للمشاركات",
"items-in-cache": "العناصر في التخزين المؤقت",
"control-panel": "لوحة التحكم",
"update-settings": "تحديث إعدادات التخزين المؤقت"
}

View File

@@ -1,52 +0,0 @@
{
"x-b": "%1 b",
"x-mb": "%1 mb",
"x-gb": "%1 gb",
"uptime-seconds": "Uptime in Seconds",
"uptime-days": "Uptime in Days",
"mongo": "Mongo",
"mongo.version": "MongoDB Version",
"mongo.storage-engine": "Storage Engine",
"mongo.collections": "Collections",
"mongo.objects": "Objects",
"mongo.avg-object-size": "Avg. Object Size",
"mongo.data-size": "Data Size",
"mongo.storage-size": "Storage Size",
"mongo.index-size": "Index Size",
"mongo.file-size": "File Size",
"mongo.resident-memory": "Resident Memory",
"mongo.virtual-memory": "الذاكرة الإفتراضية",
"mongo.mapped-memory": "Mapped Memory",
"mongo.bytes-in": "Bytes In",
"mongo.bytes-out": "Bytes Out",
"mongo.num-requests": "Number of Requests",
"mongo.raw-info": "MongoDB Raw Info",
"mongo.unauthorized": "NodeBB was unable to query the MongoDB database for relevant statistics. Please ensure that the user in use by NodeBB contains the &quot;clusterMonitor&quot; role for the &quot;admin&quot; database.",
"redis": "Redis",
"redis.version": "Redis Version",
"redis.keys": "Keys",
"redis.expires": "Expires",
"redis.avg-ttl": "Average TTL",
"redis.connected-clients": "Connected Clients",
"redis.connected-slaves": "Connected Slaves",
"redis.blocked-clients": "Blocked Clients",
"redis.used-memory": "الذاكرة المستخدمة",
"redis.memory-frag-ratio": "Memory Fragmentation Ratio",
"redis.total-connections-recieved": "إجمالي الاتصالات المستلمة",
"redis.total-commands-processed": "إجمالي الأوامر التي تمت معالجتها",
"redis.iops": "Instantaneous Ops. Per Second",
"redis.iinput": "Instantaneous Input Per Second",
"redis.ioutput": "Instantaneous Output Per Second",
"redis.total-input": "Total Input",
"redis.total-output": "Total Ouput",
"redis.keyspace-hits": "Keyspace Hits",
"redis.keyspace-misses": "Keyspace Misses",
"redis.raw-info": "Redis Raw Info",
"postgres": "Postgres",
"postgres.version": "PostgreSQL Version",
"postgres.raw-info": "Postgres Raw Info"
}

View File

@@ -1,14 +0,0 @@
{
"figure-x": "شكل %1",
"error-events-per-day": "<code>%1</code> حدث كل يوم ",
"error.404": "404 لم يتم العثور",
"error.503": "503 الخدمة غير متوفرة",
"manage-error-log": "إدارة سجل الأخطاء",
"export-error-log": "تصدير سجل الأخطاء (CSV)",
"clear-error-log": "محو سجل الأخطاء",
"route": "مسار",
"count": "عدد",
"no-routes-not-found": "لا توجد اخطاء 404!",
"clear404-confirm": "هل تريد بالتأكيد محو سجلات الخطأ 404؟",
"clear404-success": "أخطاء \"404 لم يتم العثور\" تم محوها بنجاح"
}

View File

@@ -1,11 +0,0 @@
{
"events": "أحداث",
"no-events": "لا توجد أحداث",
"control-panel": "لوحة تحكم الأحداث",
"filters": "Filters",
"filters-apply": "Apply Filters",
"filter-type": "Event Type",
"filter-start": "Start Date",
"filter-end": "End Date",
"filter-perPage": "Per Page"
}

View File

@@ -1,7 +0,0 @@
{
"logs": "السجلات",
"control-panel": "لوحة تحكم السجلات",
"reload": "إعادة تحميل السجلات",
"clear": "محو السجلات",
"clear-success": "تم محو السجلات!"
}

View File

@@ -1,16 +0,0 @@
{
"custom-css": "Custom CSS/LESS",
"custom-css.description": "Enter your own CSS/LESS declarations here, which will be applied after all other styles.",
"custom-css.enable": "Enable Custom CSS/LESS",
"custom-js": "Javascript مخصصة",
"custom-js.description": "أدخل Javascript الخاص بك هنا. سيتم تنفيذها بعد تحميل الصفحة بالكامل.",
"custom-js.enable": "تفعيل Javascript المخصصة",
"custom-header": "ترويسة مخصصة",
"custom-header.description": "Enter custom HTML here (ex. Meta Tags, etc.), which will be appended to the <code>&lt;head&gt;</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-css.livereload": "تفعيل إعادة التحميل المباشرة",
"custom-css.livereload.description": "فعل هذا الخيار لإجبار جميع الجلسات في الأجهزة التي قمت بتسجيل الدخول فيها بحسابك على التحديث عند النقر على زر الحفظ"
}

View File

@@ -1,9 +0,0 @@
{
"loading": "جاري تحميل السمات...",
"homepage": "الصفحة الرئيسية",
"select-skin": "إختيار السمة",
"current-skin": "السمة الحالية",
"skin-updated": "تم تحديث السمة",
"applied-success": "تم تطبيق السمة %1 بنجاح",
"revert-success": "تم إستعادة الألوان الاساسية للسمة"
}

View File

@@ -1,11 +0,0 @@
{
"checking-for-installed": "جاري التحقق من القوالب المثبتة...",
"homepage": "الصفحة الرئيسية",
"select-theme": "إختيار القالب",
"current-theme": "القالب المستخدم حالياً",
"no-themes": "لم يتم العثور على قوالب مثبتة",
"revert-confirm": "هل أنت متأكد من أنك ترغب في استعادة قااب NodeBB الافتراضي؟",
"theme-changed": "تم تغيير القالب",
"revert-success": "لقد قمت بنجاح بإستعادة القالب الأساسي لـNodeBB",
"restart-to-activate": "Please rebuild and restart your NodeBB to fully activate this theme."
}

View File

@@ -1,19 +0,0 @@
{
"you-are-on": "Info - You are on <strong>%1:%2</strong>",
"nodes-responded": "%1 nodes responded within %2ms!",
"host": "host",
"pid": "pid",
"nodejs": "nodejs",
"online": "online",
"git": "git",
"memory": "memory",
"load": "system load",
"cpu-usage": "cpu usage",
"uptime": "uptime",
"registered": "Registered",
"sockets": "Sockets",
"guests": "Guests",
"info": "Info"
}

View File

@@ -1,12 +0,0 @@
{
"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.",
"explanation": "Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.",
"enable-http": "Enable HTTP logging",
"enable-socket": "Enable socket.io event logging",
"file-path": "Path to log file",
"file-path-placeholder": "/path/to/log/file.log ::: leave blank to log to your terminal",
"control-panel": "Logger Control Panel",
"update-settings": "Update Logger Settings"
}

View File

@@ -1,56 +0,0 @@
{
"installed": "منصبة",
"active": "مفعلة",
"inactive": "معطلة",
"out-of-date": "غير محدثة",
"none-found": "لم يتم العثور على إضافات",
"none-active": "لا توجد إضافات مفعلة",
"find-plugins": "العثور على الإضافات",
"plugin-search": "البحث عن الإضافات",
"plugin-search-placeholder": "جاري البحث عن الإضافات...",
"submit-anonymous-usage": "Submit anonymous plugin usage data.",
"reorder-plugins": "إعادة ترتيب الإضافات",
"order-active": "ترتيب الإضافات المفعلة",
"dev-interested": "هل انته مهتم ببرمجة إضافات لـNodeBB؟",
"docs-info": "دليل كامل حول برمجة الإضافات بالإمكان العثور عليه في <a target=\"_blank\" href=\"https://docs.nodebb.org/development/plugins/\"> NodeBB Docs Portal</a>.",
"order.description": "بعض الإضافات تعمل بشكل مثالي عندما يتم تفعيلها قبل أو بعد الإضافات الأخرى.",
"order.explanation": "يتم تحميل الإضافات حسب الترتيب المحدد هنا، من الأعلى إلى الأسفل",
"plugin-item.themes": "القوالب",
"plugin-item.deactivate": "تعطيل",
"plugin-item.activate": "تفعيل",
"plugin-item.install": "تنصيب",
"plugin-item.uninstall": "إلغاء التنصيب",
"plugin-item.settings": "الإعدادات",
"plugin-item.installed": "المنصبة",
"plugin-item.latest": "الأحدث",
"plugin-item.upgrade": "ترقية",
"plugin-item.more-info": "لمزيد من المعلومات:",
"plugin-item.unknown": "غير معروف",
"plugin-item.unknown-explanation": "تعذر تحديد حالة هذه الإضافة، ربما بسبب خطأ في الإعدادات.",
"plugin-item.compatible": "This plugin works on NodeBB %1",
"plugin-item.not-compatible": "This plugin has no compatibility data, make sure it works before installing on your production environment.",
"alert.enabled": "الإضافة مفعلة",
"alert.disabled": "الإضافة معطلة",
"alert.upgraded": "الإضافة مرقاة",
"alert.installed": "الإضافة منصبة",
"alert.uninstalled": "تم إلغاء تنصيب الإضافة",
"alert.activate-success": "يرجى إعادة تشغيل NodeBB لتنشيط الإضافة بشكل بالكامل",
"alert.deactivate-success": "تم تعطيل الإضافة بنجاح",
"alert.upgrade-success": "Please rebuild and restart your NodeBB to fully upgrade this plugin.",
"alert.install-success": "تم تثبيت الإضافة بنجاح، يرجى تفعيلها.",
"alert.uninstall-success": "تم تعطيل الإضافة وإلغاء تنصيبها بنجاح.",
"alert.suggest-error": "<p>NodeBB could not reach the package manager, proceed with installation of latest version?</p><div class=\"alert alert-danger\"><strong>Server returned (%1)</strong>: %2</div>",
"alert.package-manager-unreachable": "<p>NodeBB could not reach the package manager, an upgrade is not suggested at this time.</p>",
"alert.incompatible": "<p>Your version of NodeBB (v%1) is only cleared to upgrade to v%2 of this plugin. Please update your NodeBB if you wish to install a newer version of this plugin.</p>",
"alert.possibly-incompatible": "<div class=\"alert alert-warning\"><p><strong>No Compatibility Information Found</strong></p><p>This plugin did not specify a specific version for installation given your NodeBB version. Full compatibility cannot be guaranteed, and may cause your NodeBB to no longer start properly.</p></div><p>In the event that NodeBB cannot boot properly:</p><pre><code>$ ./nodebb reset plugin=\"%1\"</code></pre><p>Continue installation of latest version of this plugin?</p>",
"alert.reorder": "Plugins Re-ordered",
"alert.reorder-success": "Please rebuild and restart your NodeBB to fully complete the process.",
"license.title": "معلومات ترخيص الإضافة",
"license.intro": "The plugin <strong>%1</strong> is licensed under the %2. Please read and understand the license terms prior to activating this plugin.",
"license.cta": "هل ترغب بالاستمرار في تفعيل هذه الإضافة؟"
}

View File

@@ -1,17 +0,0 @@
{
"rewards": "Rewards",
"condition-if-users": "If User's",
"condition-is": "Is:",
"condition-then": "Then:",
"max-claims": "Amount of times reward is claimable",
"zero-infinite": "Enter 0 for infinite",
"delete": "Delete",
"enable": "Enable",
"disable": "Disable",
"control-panel": "Rewards Control",
"new-reward": "New Reward",
"alert.delete-success": "Successfully deleted reward",
"alert.no-inputs-found": "Illegal reward - no inputs found!",
"alert.save-success": "Successfully saved rewards"
}

View File

@@ -1,30 +0,0 @@
{
"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.",
"none-installed": "No widgets found! Activate the essential widgets plugin in the <a href=\"%1\">plugins</a> control panel.",
"clone-from": "Clone widgets from",
"containers.available": "Available Containers",
"containers.explanation": "Drag and drop on top of any active widget",
"containers.none": "None",
"container.well": "Well",
"container.jumbotron": "Jumbotron",
"container.panel": "Panel",
"container.panel-header": "Panel Header",
"container.panel-body": "Panel Body",
"container.alert": "Alert",
"alert.confirm-delete": "Are you sure you wish to delete this widget?",
"alert.updated": "Widgets Updated",
"alert.update-success": "Successfully updated widgets",
"alert.clone-success": "Successfully cloned widgets",
"error.select-clone": "Please select a page to clone from",
"title": "Title",
"title.placeholder": "Title (only shown on some containers)",
"container": "Container",
"container.placeholder": "Drag and drop a container or enter HTML here.",
"show-to-groups": "Show to groups",
"hide-from-groups": "Hide from groups",
"hide-on-mobile": "Hide on mobile"
}

View File

@@ -1,79 +0,0 @@
{
"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 &amp; 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"
}

View File

@@ -1,8 +0,0 @@
{
"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\")"
}

View File

@@ -1,6 +0,0 @@
{
"language-settings": "اعدادات اللغة",
"description": "تُحدد اللغة الافتراضية إعدادات اللغة لجميع المستخدمين الذين يزورون المنتدى. <br />يمكن للأعضاء تجاوز اللغة الافتراضية من خلال صفحة إعدادات الحساب الخاصة بهم.",
"default-language": "اللغة الافتراضية",
"auto-detect": "الكشف عن إعدادات اللغة للزوار بشكل آلي"
}

View File

@@ -1,23 +0,0 @@
{
"icon": "Icon:",
"change-icon": "change",
"route": "Route:",
"tooltip": "Tooltip:",
"text": "Text:",
"text-class": "Text Class: <small>optional</small>",
"class": "Class: <small>optional</small>",
"id": "ID: <small>optional</small>",
"properties": "Properties:",
"groups": "Groups:",
"open-new-window": "Open in a new window",
"btn.delete": "Delete",
"btn.disable": "Disable",
"btn.enable": "Enable",
"available-menu-items": "Available Menu Items",
"custom-route": "Custom Route",
"core": "core",
"plugin": "plugin"
}

View File

@@ -1,5 +0,0 @@
{
"post-sharing": "Post Sharing",
"info-plugins-additional": "Plugins can add additional networks for sharing posts.",
"save-success": "Successfully saved Post Sharing Networks!"
}

View File

@@ -1,9 +0,0 @@
{
"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"
}

View File

@@ -1,10 +0,0 @@
{
"administrators": "Administrators",
"global-moderators": "Global Moderators",
"no-global-moderators": "No Global Moderators",
"moderators-of-category": "%1 Moderators",
"no-moderators": "No Moderators",
"add-administrator": "Add Administrator",
"add-global-moderator": "Add Global Moderator",
"add-moderator": "Add Moderator"
}

View File

@@ -1,81 +0,0 @@
{
"settings": "اعدادات القسم",
"privileges": "الصلاحيات",
"name": "Category Name",
"description": "Category Description",
"bg-color": "Background Colour",
"text-color": "Text Colour",
"bg-image-size": "Background Image Size",
"custom-class": "Custom Class",
"num-recent-replies": "# of Recent Replies",
"ext-link": "External Link",
"is-section": "Treat this category as a section",
"upload-image": "Upload Image",
"delete-image": "Remove",
"category-image": "Category Image",
"parent-category": "Parent Category",
"optional-parent-category": "(Optional) Parent Category",
"parent-category-none": "(None)",
"copy-parent": "Copy Parent",
"copy-settings": "Copy Settings From",
"optional-clone-settings": "(Optional) Clone Settings From Category",
"clone-children": "Clone Children Categories And Settings",
"purge": "Purge Category",
"enable": "Enable",
"disable": "Disable",
"edit": "Edit",
"select-category": "Select Category",
"set-parent-category": "Set Parent Category",
"privileges.description": "You can configure the access control privileges for portions of the site in this section. Privileges can be granted on a per-user or a per-group basis. Select the domain of effect from the dropdown below.",
"privileges.category-selector": "Configuring privileges for ",
"privileges.warning": "<strong>Note</strong>: Privilege settings take effect immediately. It is not necessary to save the category after adjusting these settings.",
"privileges.section-viewing": "Viewing Privileges",
"privileges.section-posting": "Posting Privileges",
"privileges.section-moderation": "Moderation Privileges",
"privileges.section-other": "Other",
"privileges.section-user": "User",
"privileges.search-user": "Add User",
"privileges.no-users": "No user-specific privileges in this category.",
"privileges.section-group": "Group",
"privileges.group-private": "This group is private",
"privileges.search-group": "Add Group",
"privileges.copy-to-children": "Copy to Children",
"privileges.copy-from-category": "Copy from Category",
"privileges.copy-privileges-to-all-categories": "Copy to All Categories",
"privileges.copy-group-privileges-to-children": "Copy this group's privileges to the children of this category.",
"privileges.copy-group-privileges-to-all-categories": "Copy this group's privileges to all categories.",
"privileges.copy-group-privileges-from": "Copy this group's privileges from another category.",
"privileges.inherit": "If the <code>registered-users</code> group is granted a specific privilege, all other groups receive an <strong>implicit privilege</strong>, even if they are not explicitly defined/checked. This implicit privilege is shown to you because all users are part of the <code>registered-users</code> user group, and so, privileges for additional groups need not be explicitly granted.",
"privileges.copy-success": "Privileges copied!",
"analytics.back": "Back to Categories List",
"analytics.title": "Analytics for \"%1\" category",
"analytics.pageviews-hourly": "<strong>Figure 1</strong> &ndash; Hourly page views for this category</small>",
"analytics.pageviews-daily": "<strong>Figure 2</strong> &ndash; Daily page views for this category</small>",
"analytics.topics-daily": "<strong>Figure 3</strong> &ndash; Daily topics created in this category</small>",
"analytics.posts-daily": "<strong>Figure 4</strong> &ndash; Daily posts made in this category</small>",
"alert.created": "Created",
"alert.create-success": "Category successfully created!",
"alert.none-active": "You have no active categories.",
"alert.create": "Create a Category",
"alert.confirm-moderate": "<strong>Are you sure you wish to grant the moderation privilege to this user group?</strong> This group is public, and any users can join at will.",
"alert.confirm-purge": "<p class=\"lead\">Do you really want to purge this category \"%1\"?</p><h5><strong class=\"text-danger\">Warning!</strong> All topics and posts in this category will be purged!</h5> <p class=\"help-block\">Purging a category will remove all topics and posts, and delete the category from the database. If you want to remove a category <em>temporarily</em>, you'll want to \"disable\" the category instead.</p>",
"alert.purge-success": "Category purged!",
"alert.copy-success": "Settings Copied!",
"alert.set-parent-category": "Set Parent Category",
"alert.updated": "Updated Categories",
"alert.updated-success": "Category IDs %1 successfully updated.",
"alert.upload-image": "Upload category image",
"alert.find-user": "Find a User",
"alert.user-search": "Search for a user here...",
"alert.find-group": "Find a Group",
"alert.group-search": "Search for a group here...",
"collapse-all": "Collapse All",
"expand-all": "Expand All",
"disable-on-create": "Disable on create"
}

View File

@@ -1,21 +0,0 @@
{
"lead": "A listing of digest delivery stats and times is displayed below.",
"disclaimer": "Please be advised that email delivery is not guaranteed, due to the nature of email technology. Many variables factor into whether an email sent to the recipient server is ultimately delivered into the user's inbox, including server reputation, blacklisted IP addresses, and whether DKIM/SPF/DMARC is configured.",
"disclaimer-continued": "A successful delivery means the message was sent successfully by NodeBB and acknowledged by the recipient server. It does not mean the email landed in the inbox. For best results, we recommend using a third-party email delivery service such as <a href=\"https://sendgrid.com/why-sendgrid/\">SendGrid</a>.",
"user": "User",
"subscription": "Subscription Type",
"last-delivery": "Last successful delivery",
"default": "System default",
"default-help": "<em>System default</em> means the user has not explicitly overridden the global forum setting for digests, which is currently: &quot;<strong>%1</strong>&quot;",
"resend": "Resend Digest",
"resend-all-confirm": "Are you sure you wish to mnually execute this digest run?",
"resent-single": "Manual digest resend completed",
"resent-day": "Daily digest resent",
"resent-week": "Weekly digest resent",
"resent-month": "Monthly digest resent",
"null": "<em>Never</em>",
"manual-run": "Manual digest run:",
"no-delivery-data": "No delivery data found"
}

View File

@@ -1,41 +0,0 @@
{
"name": "اسم المجموعة",
"badge": "Badge",
"properties": "Properties",
"description": "Group Description",
"member-count": "Member Count",
"system": "System",
"hidden": "Hidden",
"private": "Private",
"edit": "Edit",
"search-placeholder": "Search",
"create": "Create Group",
"description-placeholder": "A short description about your group",
"create-button": "Create",
"alerts.create-failure": "<strong>Uh-Oh</strong><p>There was a problem creating your group. Please try again later!</p>",
"alerts.confirm-delete": "Are you sure you wish to delete this group?",
"edit.name": "Name",
"edit.description": "Description",
"edit.user-title": "Title of Members",
"edit.icon": "Group Icon",
"edit.label-color": "Group Label Color",
"edit.text-color": "Group Text Color",
"edit.show-badge": "Show Badge",
"edit.private-details": "If enabled, joining of groups requires approval from a group owner.",
"edit.private-override": "Warning: Private groups is disabled at system level, which overrides this option.",
"edit.disable-join": "Disable join requests",
"edit.disable-leave": "Disallow users from leaving the group",
"edit.hidden": "Hidden",
"edit.hidden-details": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually",
"edit.add-user": "Add User to Group",
"edit.add-user-search": "Search Users",
"edit.members": "Member List",
"control-panel": "Groups Control Panel",
"revert": "Revert",
"edit.no-users-found": "No Users Found",
"edit.confirm-remove-user": "Are you sure you want to remove this user?",
"edit.save-success": "Changes saved!"
}

View File

@@ -1,19 +0,0 @@
{
"lead": "Configure your IP blacklist here.",
"description": "Occasionally, a user account ban is not enough of a deterrant. Other times, restricting access to the forum to a specific IP or a range of IPs is the best way to protect a forum. In these scenarios, you can add troublesome IP addresses or entire CIDR blocks to this blacklist, and they will be prevented from logging in to or registering a new account.",
"active-rules": "Active Rules",
"validate": "Validate Blacklist",
"apply": "Apply Blacklist",
"hints": "Syntax Hints",
"hint-1": "Define a single IP addresses per line. You can add IP blocks as long as they follow the CIDR format (e.g. <code>192.168.100.0/22</code>).",
"hint-2": "You can add in comments by starting lines with the <code>#</code> symbol.",
"validate.x-valid": "<strong>%1</strong> out of <strong>%2</strong> rule(s) valid.",
"validate.x-invalid": "The following <strong>%1</strong> rules are invalid:",
"alerts.applied-success": "Blacklist Applied",
"analytics.blacklist-hourly": "<strong>Figure 1</strong> &ndash; Blacklist hits per hour",
"analytics.blacklist-daily": "<strong>Figure 2</strong> &ndash; Blacklist hits per day",
"ip-banned": "IP banned"
}

View File

@@ -1,11 +0,0 @@
{
"post-queue": "Post Queue",
"description": "There are no posts in the post queue. <br> To enable this feature, go to <a href=\"%1\">Settings &rarr; Post &rarr; Post Queue</a> and enable <strong>Post Queue</strong>.",
"user": "User",
"category": "Category",
"title": "Title",
"content": "Content",
"posted": "Posted",
"reply-to": "Reply to \"%1\"",
"content-editable": "You can click on individual content to edit before posting."
}

View File

@@ -1,35 +0,0 @@
{
"global": "Global",
"global.no-users": "No user-specific global privileges.",
"group-privileges": "Group Privileges",
"user-privileges": "User Privileges",
"chat": "Chat",
"upload-images": "Upload Images",
"upload-files": "Upload Files",
"signature": "Signature",
"ban": "Ban",
"search-content": "Search Content",
"search-users": "Search Users",
"search-tags": "Search Tags",
"view-users": "View Users",
"view-tags": "View Tags",
"view-groups": "View Groups",
"allow-local-login": "Local Login",
"allow-group-creation": "Group Create",
"view-users-info": "View Users Info",
"find-category": "Find Category",
"access-category": "Access Category",
"access-topics": "Access Topics",
"create-topics": "Create Topics",
"reply-to-topics": "Reply to Topics",
"tag-topics": "Tag Topics",
"edit-posts": "Edit Posts",
"view-edit-history": "View Edit History",
"delete-posts": "Delete Posts",
"view_deleted": "View Deleted Posts",
"upvote-posts": "Upvote Posts",
"downvote-posts": "Downvote Posts",
"delete-topics": "Delete Topics",
"purge": "Purge",
"moderate": "Moderate"
}

View File

@@ -1,20 +0,0 @@
{
"queue": "Queue",
"description": "There are no users in the registration queue. <br> To enable this feature, go to <a href=\"%1\">Settings &rarr; User &rarr; User Registration</a> and set <strong>Registration Type</strong> to \"Admin Approval\".",
"list.name": "Name",
"list.email": "Email",
"list.ip": "IP",
"list.time": "Time",
"list.username-spam": "Frequency: %1 Appears: %2 Confidence: %3",
"list.email-spam": "Frequency: %1 Appears: %2",
"list.ip-spam": "Frequency: %1 Appears: %2",
"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.inviter-username": "Inviter Username",
"invitations.invitee-email": "Invitee Email",
"invitations.invitee-username": "Invitee Username (if registered)",
"invitations.confirm-delete": "Are you sure you wish to delete this invitation?"
}

View File

@@ -1,19 +0,0 @@
{
"none": "Your forum does not have any topics with tags yet.",
"bg-color": "Background Colour",
"text-color": "Text Colour",
"create-modify": "Create & Modify Tags",
"description": "Select tags via clicking and/or dragging, use shift to select multiple.",
"create": "Create Tag",
"modify": "Modify Tags",
"rename": "Rename Tags",
"delete": "Delete Selected Tags",
"search": "Search for tags...",
"settings": "Click <a href=\"%1\">here</a> to visit the tag settings page.",
"name": "Tag Name",
"alerts.editing-multiple": "Editing multiple tags",
"alerts.editing-x": "Editing \"%1\" tag",
"alerts.confirm-delete": "Do you want to delete the selected tags?",
"alerts.update-success": "Tag Updated!"
}

View File

@@ -1,9 +0,0 @@
{
"upload-file": "Upload File",
"filename": "Filename",
"usage": "Post Usage",
"orphaned": "Orphaned",
"size/filecount": "Size / Filecount",
"confirm-delete": "Do you really want to delete this file?",
"filecount": "%1 files"
}

View File

@@ -1,109 +0,0 @@
{
"users": "المستخدمين",
"edit": "تحرير",
"make-admin": "Make Admin",
"remove-admin": "Remove Admin",
"validate-email": "Validate Email",
"send-validation-email": "Send Validation Email",
"password-reset-email": "Send Password Reset Email",
"force-password-reset": "Force Password Reset & Log User Out",
"ban": "Ban User(s)",
"temp-ban": "Ban User(s) Temporarily",
"unban": "Unban User(s)",
"reset-lockout": "Reset Lockout",
"reset-flags": "Reset Flags",
"delete": "Delete User(s)",
"purge": "Delete User(s) and Content",
"download-csv": "Download CSV",
"manage-groups": "Manage Groups",
"add-group": "Add Group",
"invite": "Invite",
"new": "New User",
"pills.latest": "Latest Users",
"pills.unvalidated": "Not Validated",
"pills.no-posts": "No Posts",
"pills.top-posters": "Top Posters",
"pills.top-rep": "Most Reputation",
"pills.inactive": "Inactive",
"pills.flagged": "Most Flagged",
"pills.banned": "Banned",
"pills.search": "User Search",
"50-per-page": "50 per page",
"100-per-page": "100 per page",
"250-per-page": "250 per page",
"500-per-page": "500 per page",
"search.uid": "By User ID",
"search.uid-placeholder": "Enter a user ID to search",
"search.username": "By User Name",
"search.username-placeholder": "Enter a username to search",
"search.email": "By Email",
"search.email-placeholder": "Enter a email to search",
"search.ip": "By IP Address",
"search.ip-placeholder": "Enter an IP Address to search",
"search.not-found": "User not found!",
"inactive.3-months": "3 months",
"inactive.6-months": "6 months",
"inactive.12-months": "12 months",
"users.uid": "uid",
"users.username": "username",
"users.email": "email",
"users.postcount": "postcount",
"users.reputation": "reputation",
"users.flags": "flags",
"users.joined": "joined",
"users.last-online": "last online",
"users.banned": "banned",
"create.username": "User Name",
"create.email": "Email",
"create.email-placeholder": "Email of this user",
"create.password": "Password",
"create.password-confirm": "Confirm Password",
"temp-ban.length": "Ban Length",
"temp-ban.reason": "Reason <span class=\"text-muted\">(Optional)</span>",
"temp-ban.hours": "Hours",
"temp-ban.days": "Days",
"temp-ban.explanation": "Enter the length of time for the ban. Note that a time of 0 will be a considered a permanent ban.",
"alerts.confirm-ban": "Do you really want to ban this user <strong>permanently</strong>?",
"alerts.confirm-ban-multi": "Do you really want to ban these users <strong>permanently</strong>?",
"alerts.ban-success": "User(s) banned!",
"alerts.button-ban-x": "Ban %1 user(s)",
"alerts.unban-success": "User(s) unbanned!",
"alerts.lockout-reset-success": "Lockout(s) reset!",
"alerts.flag-reset-success": "Flags(s) reset!",
"alerts.no-remove-yourself-admin": "You can't remove yourself as Administrator!",
"alerts.make-admin-success": "User is now administrator.",
"alerts.confirm-remove-admin": "Do you really want to remove this administrator?",
"alerts.remove-admin-success": "User is no longer administrator.",
"alerts.make-global-mod-success": "User is now global moderator.",
"alerts.confirm-remove-global-mod": "Do you really want to remove this global moderator?",
"alerts.remove-global-mod-success": "User is no longer global moderator.",
"alerts.make-moderator-success": "User is now moderator.",
"alerts.confirm-remove-moderator": "Do you really want to remove this moderator?",
"alerts.remove-moderator-success": "User is no longer moderator.",
"alerts.confirm-validate-email": "Do you want to validate email(s) of these user(s)?",
"alerts.confirm-force-password-reset": "Are you sure you want to force the password reset and log out these user(s)?",
"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.password-reset-confirm": "Do you want to send password reset email(s) to these user(s)?",
"alerts.confirm-delete": "<b>Warning!</b><br/>Do you really want to delete user(s)?<br/> This action is not reversable! Only the user account will be deleted, their posts and topics will remain.",
"alerts.delete-success": "User(s) Deleted!",
"alerts.confirm-purge": "<b>Warning!</b><br/>Do you really want to delete user(s) and their content?<br/> This action is not reversable! All user data and content will be erased!",
"alerts.create": "Create User",
"alerts.button-create": "Create",
"alerts.button-cancel": "Cancel",
"alerts.error-passwords-different": "Passwords must match!",
"alerts.error-x": "<strong>Error</strong><p>%1</p>",
"alerts.create-success": "User created!",
"alerts.prompt-email": "Emails: ",
"alerts.email-sent-to": "An invitation email has been sent to %1",
"alerts.x-users-found": "%1 user(s) found! Search took %2 ms."
}

View File

@@ -1,83 +0,0 @@
{
"section-general": "عام",
"general/dashboard": "اللوحة الرئيسية",
"general/homepage": "الصفحة الرئيسية",
"general/navigation": "التصفح",
"general/languages": "اللغات",
"general/sounds": "الأصوات",
"general/social": "شبكات التواصل",
"section-manage": "إدارة",
"manage/categories": "الأقسام",
"manage/privileges": "Privileges",
"manage/tags": "الكلمات المفتاحية",
"manage/users": "الأعضاء",
"manage/admins-mods": "Admins & Mods",
"manage/registration": "قائمة انتظار التسجيل",
"manage/post-queue": "قائمة انتظار المشاركة",
"manage/groups": "المجموعات",
"manage/ip-blacklist": "قائمة حظر عناوين IP",
"manage/uploads": "Uploads",
"manage/digest": "Digests",
"section-settings": "إعدادات",
"settings/general": "عامة",
"settings/reputation": "السمعة",
"settings/email": "البريد الإلكتروني",
"settings/user": "الأعضاء",
"settings/group": "المجموعات",
"settings/guest": "الزوار",
"settings/uploads": "الرفع",
"settings/post": "المشاركة",
"settings/chat": "الدردشة",
"settings/pagination": "ترقيم الصفحات",
"settings/tags": "الكلمات المفتاحية",
"settings/notifications": "التنبيهات",
"settings/cookies": "Cookies",
"settings/web-crawler": "Web Crawler",
"settings/sockets": "Sockets",
"settings/advanced": "Advanced",
"settings.page-title": "%1 Settings",
"section-appearance": "Appearance",
"appearance/themes": "Themes",
"appearance/skins": "Skins",
"appearance/customise": "Custom Content (HTML/JS/CSS)",
"section-extend": "Extend",
"extend/plugins": "Plugins",
"extend/widgets": "Widgets",
"extend/rewards": "Rewards",
"section-social-auth": "Social Authentication",
"section-plugins": "Plugins",
"extend/plugins.install": "Install Plugins",
"section-advanced": "Advanced",
"advanced/database": "Database",
"advanced/events": "Events",
"advanced/hooks": "Hooks",
"advanced/logs": "Logs",
"advanced/errors": "Errors",
"advanced/cache": "Cache",
"development/logger": "Logger",
"development/info": "Info",
"rebuild-and-restart-forum": "Rebuild & Restart Forum",
"restart-forum": "Restart Forum",
"logout": "Log out",
"view-forum": "View Forum",
"search.placeholder": "Search for settings",
"search.no-results": "No results...",
"search.search-forum": "Search the forum for <strong></strong>",
"search.keep-typing": "Type more to see results...",
"search.start-typing": "Start typing to see results...",
"connection-lost": "Connection to %1 has been lost, attempting to reconnect...",
"alerts.version": "Running <strong>NodeBB v%1</strong>",
"alerts.upgrade": "Upgrade to v%1"
}

View File

@@ -1,28 +0,0 @@
{
"maintenance-mode": "Maintenance Mode",
"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.message": "Maintenance Message",
"headers": "Headers",
"headers.allow-from": "Set ALLOW-FROM to Place NodeBB in an iFrame",
"headers.powered-by": "Customise the \"Powered By\" header sent by NodeBB",
"headers.acao": "Access-Control-Allow-Origin",
"headers.acao-regex": "Access-Control-Allow-Origin Regular Expression",
"headers.acao-help": "To deny access to all sites, leave empty",
"headers.acao-regex-help": "Enter regular expressions here to match dynamic origins. To deny access to all sites, leave empty",
"headers.acac": "Access-Control-Allow-Credentials",
"headers.acam": "Access-Control-Allow-Methods",
"headers.acah": "Access-Control-Allow-Headers",
"hsts": "Strict Transport Security",
"hsts.enabled": "Enabled HSTS (recommended)",
"hsts.subdomains": "Include subdomains in HSTS header",
"hsts.preload": "Allow preloading of HSTS header",
"hsts.help": "If enabled, an HSTS header will be set for this site. You can elect to include subdomains and preloading flags in your header. If in doubt, you can leave these unchecked. <a href=\"%1\">More information <i class=\"fa fa-external-link\"></i></a>",
"traffic-management": "Traffic Management",
"traffic.help": "NodeBB deploys equipped with a module that automatically denies requests in high-traffic situations. You can tune these settings here, although the defaults are a good starting point.",
"traffic.enable": "Enable Traffic Management",
"traffic.event-lag": "Event Loop Lag Threshold (in milliseconds)",
"traffic.event-lag-help": "Lowering this value decreases wait times for page loads, but will also show the \"excessive load\" message to more users. (Restart required)",
"traffic.lag-check-interval": "Check Interval (in milliseconds)",
"traffic.lag-check-interval-help": "Lowering this value causes NodeBB to become more sensitive to spikes in load, but may also cause the check to become too sensitive. (Restart required)"
}

View File

@@ -1,11 +0,0 @@
{
"chat-settings": "Chat Settings",
"disable": "Disable chat",
"disable-editing": "Disable chat message editing/deletion",
"disable-editing-help": "Administrators and global moderators are exempt from this restriction",
"max-length": "Maximum length of chat messages",
"max-room-size": "Maximum number of users in chat rooms",
"delay": "Time between chat messages in milliseconds",
"restrictions.seconds-edit-after": "Number of seconds a chat message will remain editable. (0 disabled)",
"restrictions.seconds-delete-after": "Number of seconds a chat message will remain deletable. (0 disabled)"
}

View File

@@ -1,12 +0,0 @@
{
"eu-consent": "EU Consent",
"consent.enabled": "تفعيل",
"consent.message": "Notification message",
"consent.acceptance": "Acceptance message",
"consent.link-text": "Policy Link Text",
"consent.link-url": "Policy Link URL",
"consent.blank-localised-default": "Leave blank to use NodeBB localised defaults",
"settings": "Settings",
"cookie-domain": "Session cookie domain",
"blank-default": "Leave blank for default"
}

View File

@@ -1,40 +0,0 @@
{
"email-settings": "Email Settings",
"address": "Email Address",
"address-help": "The following email address refers to the email that the recipient will see in the \"From\" and \"Reply To\" fields.",
"from": "From Name",
"from-help": "The from name to display in the email.",
"sendmail-rate-limit": "Send <em>X</em> emails...",
"sendmail-rate-delta": "... every <em>X</em> milliseconds",
"sendmail-rate-help": "Instructs the NodeBB mailer to limit the number of messages sent at once in order to not overwhelm email receiving services. These options do not apply if SMTP Transport is enabled (below).",
"smtp-transport": "SMTP Transport",
"smtp-transport.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.service": "Select a 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.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 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.host": "SMTP Host",
"smtp-transport.port": "SMTP Port",
"smtp-transport.security": "Connection security",
"smtp-transport.security-encrypted": "Encrypted",
"smtp-transport.security-starttls": "StartTLS",
"smtp-transport.security-none": "None",
"smtp-transport.username": "Username",
"smtp-transport.username-help": "<b>For the Gmail service,</b> enter the full email address here, especially if you are using a Google Apps managed domain.",
"smtp-transport.password": "Password",
"template": "Edit Email Template",
"template.select": "Select Email Template",
"template.revert": "Revert to Original",
"testing": "Email Testing",
"testing.select": "Select Email Template",
"testing.send": "Send Test Email",
"testing.send-help": "The test email will be sent to the currently logged in user's email address.",
"subscriptions": "Email Digests",
"subscriptions.disable": "Disable email digests",
"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>"
}

View File

@@ -1,35 +0,0 @@
{
"site-settings": "Site Settings",
"title": "Site Title",
"title.url": "URL",
"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.",
"title.name": "Your Community Name",
"title.show-in-header": "Show Site Title in Header",
"browser-title": "Browser Title",
"browser-title-help": "If no browser title is specified, the site title will be used",
"title-layout": "Title Layout",
"title-layout-help": "Define how the browser title will be structured ie. &#123;pageTitle&#125; | &#123;browserTitle&#125;",
"description.placeholder": "A short description about your community",
"description": "Site Description",
"keywords": "Site Keywords",
"keywords-placeholder": "Keywords describing your community, comma-separated",
"logo": "Site Logo",
"logo.image": "Image",
"logo.image-placeholder": "Path to a logo to display on forum header",
"logo.upload": "Upload",
"logo.url": "URL",
"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.",
"logo.alt-text": "Alt Text",
"log.alt-text-placeholder": "Alternative text for accessibility",
"favicon": "Favicon",
"favicon.upload": "Upload",
"touch-icon": "Homescreen/Touch Icon",
"touch-icon.upload": "Upload",
"touch-icon.help": "Recommended size and format: 192x192, PNG format only. If no touch icon is specified, NodeBB will fall back to using the favicon.",
"outgoing-links": "Outgoing Links",
"outgoing-links.warning-page": "Use Outgoing Links Warning Page",
"search-default-sort-by": "Search default sort by",
"outgoing-links.whitelist": "Domains to whitelist for bypassing the warning page"
}

View File

@@ -1,12 +0,0 @@
{
"general": "General",
"private-groups": "Private Groups",
"private-groups.help": "If enabled, joining of groups requires the approval of the group owner <em>(Default: enabled)</em>",
"private-groups.warning": "<strong>Beware!</strong> If this option is disabled and you have private groups, they automatically become public.",
"allow-multiple-badges-help": "This flag can be used to allow users to select multiple group badges, requires theme support.",
"max-name-length": "Maximum Group Name Length",
"max-title-length": "Maximum Group Title Length",
"cover-image": "Group Cover Image",
"default-cover": "Default Cover Images",
"default-cover-help": "Add comma-separated default cover images for groups that don't have an uploaded cover image"
}

View File

@@ -1,5 +0,0 @@
{
"handles": "Guest Handles",
"handles.enabled": "Allow guest handles",
"handles.enabled-help": "This option exposes a new field that allows guests to pick a name to associate with each post they make. If disabled, they will simply be called \"Guest\""
}

View File

@@ -1,7 +0,0 @@
{
"notifications": "Notifications",
"welcome-notification": "Welcome Notification",
"welcome-notification-link": "Welcome Notification Link",
"welcome-notification-uid": "Welcome Notification User (UID)",
"notification-alert-timeout": "Notification Alert Timeout"
}

View File

@@ -1,10 +0,0 @@
{
"pagination": "Pagination Settings",
"enable": "Paginate topics and posts instead of using infinite scroll.",
"topics": "Topic Pagination",
"posts-per-page": "Posts per Page",
"max-posts-per-page": "Maximum posts per page",
"categories": "Category Pagination",
"topics-per-page": "Topics per Page",
"max-topics-per-page": "Maximum topics per page"
}

View File

@@ -1,59 +0,0 @@
{
"sorting": "Post Sorting",
"sorting.post-default": "Default Post Sorting",
"sorting.oldest-to-newest": "Oldest to Newest",
"sorting.newest-to-oldest": "Newest to Oldest",
"sorting.most-votes": "Most Votes",
"sorting.most-posts": "Most Posts",
"sorting.topic-default": "Default Topic Sorting",
"length": "Post Length",
"post-queue": "Post Queue",
"restrictions": "Posting Restrictions",
"restrictions-new": "New User Restrictions",
"restrictions.post-queue": "Enable 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-new.post-queue": "Enable new user restrictions",
"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.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-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.min-title-length": "Minimum Title 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.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.cut-off": "Date cut-off (in days)",
"timestamp.cut-off-help": "Dates &amp; 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.necro-threshold": "Necro Threshold (in days)",
"timestamp.necro-threshold-help": "A message will be shown between posts if the time between them is longer than the necro threshold. (Default: <code>7</code>, or one week). Set to 0 to disable.</em>",
"teaser": "Teaser Post",
"teaser.last-post": "Last &ndash; Show the latest post, including the original post, if no replies",
"teaser.last-reply": "Last &ndash; Show the latest reply, or a \"No replies\" placeholder if no replies",
"teaser.first": "First",
"unread": "Unread Settings",
"unread.cutoff": "Unread cutoff days",
"unread.min-track-last": "Minimum posts in topic before tracking last read",
"recent": "Recent Settings",
"recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page",
"signature": "Signature Settings",
"signature.disable": "Disable signatures",
"signature.no-links": "Disable links in signatures",
"signature.no-images": "Disable images in signatures",
"signature.max-length": "Maximum Signature Length",
"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.show-help": "Show \"Help\" tab",
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
"composer.custom-help": "Custom Help Text",
"ip-tracking": "IP Tracking",
"ip-tracking.each-post": "Track IP Address for each post",
"enable-post-history": "Enable Post History"
}

View File

@@ -1,14 +0,0 @@
{
"reputation": "Reputation Settings",
"disable": "Disable Reputation System",
"disable-down-voting": "Disable Down Voting",
"votes-are-public": "All Votes Are Public",
"thresholds": "Activity Thresholds",
"min-rep-downvote": "Minimum reputation to downvote 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-signature": "Minimum reputation to add \"Signature\" to user profile",
"min-rep-profile-picture": "Minimum reputation to add \"Profile Picture\" to user profile",
"min-rep-cover-picture": "Minimum reputation to add \"Cover Picture\" to user profile"
}

View File

@@ -1,6 +0,0 @@
{
"reconnection": "Reconnection Settings",
"max-attempts": "Max Reconnection Attempts",
"default-placeholder": "Default: %1",
"delay": "Reconnection Delay"
}

View File

@@ -1,10 +0,0 @@
{
"tag": "Tag Settings",
"min-per-topic": "Minimum Tags per Topic",
"max-per-topic": "Maximum Tags per Topic",
"min-length": "Minimum Tag Length",
"max-length": "Maximum Tag Length",
"goto-manage": "Click here to visit the tag management page.",
"related-topics": "Related Topics",
"max-related-topics": "Maximum related topics to display (if supported by theme)"
}

View File

@@ -1,39 +0,0 @@
{
"posts": "المشاركات",
"allow-files": "السماح للأعضاء بتحميل الملفات الإعتيادية",
"private": "جعل الملفات التي تم رفعها خاصة",
"strip-exif-data": "Strip EXIF Data",
"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.",
"resize-image-width-threshold": "Resize images if they are wider than specified width",
"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-help": "(in pixels, default: 760 pixels, set to 0 to disable)",
"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.",
"max-file-size": "الحد الأقصى لحجم الملف (بالكيبيبايت)",
"max-file-size-help": "(بالكيبيبايت، الافتراضي: 2048)",
"reject-image-width": "Maximum Image Width (in pixels)",
"reject-image-width-help": "Images wider than this value will be rejected.",
"reject-image-height": "Maximum Image Height (in pixels)",
"reject-image-height-help": "Images taller than this value will be rejected.",
"allow-topic-thumbnails": "السماح للاعضاء برفع الصور المصغرة للموضوع",
"topic-thumb-size": "حجم الصورة المصغرة للموضوع",
"allowed-file-extensions": "إمتدادات الملفات المسموح بها",
"allowed-file-extensions-help": "أدخل قائمة بامتدادات الملفات مفصولة بفواصل (مثال: <code>pdf,xls,doc</code>). القائمة الفارغة تعني أن كل الامتدادات مسموح بها.",
"profile-avatars": "الصورة الرمزية للملف الشخصي",
"allow-profile-image-uploads": "السماح للأعضاء برفع الصور الرمزية",
"convert-profile-image-png": "تحويل إمتداد الصور الرمزية المرفوعه الى PNG",
"default-avatar": "الصورة الرمزية الافتراضية",
"upload": "رفع",
"profile-image-dimension": "أبعاد الصورة الرمزية",
"profile-image-dimension-help": "(بالبكسل، الافتراضي: 128 بكسل)",
"max-profile-image-size": "الحد الأقصى لحجم الصورة الرمزية",
"max-profile-image-size-help": "(بالكيبيبايت، الافتراضي: 256)",
"max-cover-image-size": "الحد الأقصى لحجم صورة الغلاف",
"max-cover-image-size-help": "(بالكيبيبايت، الافتراضي: 2,048)",
"keep-all-user-images": "الاحتفاظ بالنسخ القديمة من الصور الرمزية وصور الغلاف في السيرفر",
"profile-covers": "غلاف الملف الشخصي",
"default-covers": "صورة الغلاف الافتراضية",
"default-covers-help": "اضف صور الغلاف الافتراضية متبوعة بفواصل لاستخدامها في الحسابات التي لا تحتوي على صور غلاف مرفوعة"
}

View File

@@ -1,79 +0,0 @@
{
"authentication": "المصادقة",
"require-email-confirmation": "يطلب تأكيد البريد الإلكتروني",
"email-confirm-interval": "لا يمكن للمستخدم إعادة إرسال رسالة تأكيد البريد الالكتروني حتى مرور",
"email-confirm-email2": "دقائق",
"allow-login-with": "السماح بتسجيل الدخول باستخدام",
"allow-login-with.username-email": "اسم المستخدم أو البريد الالكتروني",
"allow-login-with.username": "اسم المستخدم فقط",
"allow-login-with.email": "البريد الالكتروني فقط",
"account-settings": "إعدادت الحساب",
"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://eugdpr.org/the-regulation/gdpr-faqs/\">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-email-changes": "عدم السماح بتغيير البريد الالكتروني",
"disable-password-changes": "عدم السماح بتغيير كلمة المرور",
"allow-account-deletion": "السماح بحذف الحساب",
"hide-fullname": "إخفاء الإسم الكامل عن المستخدمين",
"hide-email": "إخفاء البريد الإلكتروني عن المستخدمين",
"themes": "القوالب",
"disable-user-skins": "منع المستخدمين من اختيار سمة مخصص",
"account-protection": "حماية الحساب",
"admin-relogin-duration": "المدة حتى طلب إعادة تسجيل الدخول للإدارة (دقائق)",
"admin-relogin-duration-help": "بعد مرور وقت معين، يتوجب إعادة تسجيل الدخول للوصول إلى قسم الإدارة، قم بتعيين القيمة الى 0 لتعطيل الخيار",
"login-attempts": "عدد محاولات تسجيل الدخول في الساعة",
"login-attempts-help": "إذا تجاوزت محاولات تسجيل الدخول لمستخدم معين العدد المحدد، فسوف يتم تأمين الحساب ومنعه من الدخول لمدة من الوقت",
"lockout-duration": "مدة تأمين الحساب (دقائق)",
"login-days": "عدد الأيام لتذكر جلسات تسجيل دخول المستخدم",
"password-expiry-days": "فرض عملية تغيير كلمة المرور بعد مرور عدد محدد من الأيام",
"session-time": "Session Time",
"session-time-days": "Days",
"session-time-seconds": "Seconds",
"session-time-help": "These values are used to govern how long a user stays logged in when they check &quot;Remember Me&quot; 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>.",
"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.",
"registration": "تسجيل المستخدم",
"registration-type": "نوع التسجيل",
"registration-approval-type": "Registration Approval Type",
"registration-type.normal": "عادي",
"registration-type.admin-approval": "بموافقة الإدارة",
"registration-type.admin-approval-ip": "بموافقة الإدارة لعناوين IP",
"registration-type.invite-only": "بالدعوات فقط",
"registration-type.admin-invite-only": "بالدعوات من قبل الإدارة فقط",
"registration-type.disabled": "لا يوجد تسجيل",
"registration-type.help": "Normal - Users can register from the /register page.<br/>\nInvite Only - Users can invite others from the <a href=\"%1/users\" target=\"_blank\">users</a> page.<br/>\nAdmin Invite Only - Only administrators can invite others from <a href=\"%1/users\" target=\"_blank\">users</a> and <a href=\"%1/admin/manage/users\">admin/manage/users</a> pages.<br/>\nNo registration - No user registration.<br/>",
"registration-approval-type.help": "Normal - Users are registered immediately.<br/>\nAdmin Approval - User registrations are placed in an <a href=\"%1/admin/manage/registration\">approval queue</a> for administrators.<br/>\nAdmin Approval for IPs - Normal for new users, Admin Approval for IP addresses that already have an account.<br/>",
"registration.max-invites": "الحد الأقصى للدعوات لكل عضو",
"max-invites": "الحد الأقصى للدعوات لكل عضو",
"max-invites-help": "0 لعدم تحديد قيود، الإدارة تحصل على دعوات لامحدودة <br> هذا الخيار يعمل فقط عند تحديد خيار \"بالدعوات فقط\"",
"invite-expiration": "مدة صلاحية الدعوة",
"invite-expiration-help": "عدد الأيام حتى انتهاء صلاحية الدعوة.",
"min-username-length": "الحد الأدنى لطول اسم المستخدم",
"max-username-length": "الحد الأقصى لطول اسم المستخدم",
"min-password-length": "الحد الأدنى لطول كلمة المرور",
"min-password-strength": "الحد الأدنى لقوة كلمة المرور",
"max-about-me-length": "الحد الأعلى من الأحرف في حقل \"عني\"",
"terms-of-use": "شروط استخدام المنتدى <small>(تترك فارغة لتعطيلها)</small>",
"user-search": "بحث الأعضاء",
"user-search-results-per-page": "عدد النتائج المراد عرضها",
"default-user-settings": "إعدادات الأعضاء الافتراضية",
"show-email": "عرض البريد الإلكتروني",
"show-fullname": "عرض الاسم الكامل",
"restrict-chat": "السماح فقط برسائل الدردشة من المستخدمين الذين أتبعهم",
"outgoing-new-tab": "Open outgoing links in new tab",
"topic-search": "Enable In-Topic Searching",
"digest-freq": "Subscribe to Digest",
"digest-freq.off": "Off",
"digest-freq.daily": "Daily",
"digest-freq.weekly": "Weekly",
"digest-freq.monthly": "Monthly",
"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",
"follow-created-topics": "Follow topics you create",
"follow-replied-topics": "Follow topics that you reply to",
"default-notification-settings": "Default notification settings",
"categoryWatchState": "Default category watch state",
"categoryWatchState.watching": "Watching",
"categoryWatchState.notwatching": "Not Watching",
"categoryWatchState.ignoring": "Ignoring"
}

View File

@@ -1,10 +0,0 @@
{
"crawlability-settings": "Crawlability Settings",
"robots-txt": "Custom Robots.txt <small>Leave blank for default</small>",
"sitemap-feed-settings": "Sitemap & Feed Settings",
"disable-rss-feeds": "Disable RSS Feeds",
"disable-sitemap-xml": "Disable Sitemap.xml",
"sitemap-topics": "Number of Topics to display in the Sitemap",
"clear-sitemap-cache": "Clear Sitemap Cache",
"view-sitemap": "View Sitemap"
}

View File

@@ -1,22 +1,8 @@
{ {
"category": "قسم",
"subcategories": "قسم فرعي",
"new_topic_button": "موضوع جديد", "new_topic_button": "موضوع جديد",
"guest-login-post": "سجل الدخول للمشاركة", "no_topics": "<strong>لا توجد مواضيع في هذه الفئة</strong>لماذا لا تحاول نشر موضوع؟<br />",
"no_topics": "<strong>لا توجد مواضيع في هذه القسم</strong>لم لا تحاول إنشاء موضوع؟<br />",
"browsing": "تصفح", "browsing": "تصفح",
"no_replies": "لم يرد أحد", "no_replies": "لم يرد أحد",
"no_new_posts": "لا توجد مشاركات جديدة.", "share_this_category": "انشر هذه الفئة",
"watch": "تابع", "ignore": "Ignore"
"ignore": "تجاهل",
"watching": "متابع",
"not-watching": "لست متابع",
"ignoring": "متجاهل",
"watching.description": "أظهر المواضيع في الغير مقروء و الحديث",
"not-watching.description": "Do not show topics in unread, show in recent",
"ignoring.description": "Do not show topics in unread and recent",
"watching.message": "You are now 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",
"watched-categories": "الأقسام المُتابعة"
} }

View File

@@ -1,52 +1,23 @@
{ {
"test-email.subject": "Test Email", "password-reset-requested": "Password Reset Requested - %1!",
"password-reset-requested": "Password Reset Requested!", "welcome-to": "Welcome to %1",
"welcome-to": "مرحبًا بك في %1", "greeting_no_name": "Hello",
"invite": "دعوة من %1", "greeting_with_name": "Hello %1",
"greeting_no_name": "مرحبًا", "welcome.text1": "Thank you for registering with %1!",
"greeting_with_name": "مرحبًا بك يا %1", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.",
"email.verify-your-email.subject": "Please verify your email", "welcome.cta": "Click here to confirm your email address",
"email.verify.text1": "Your email address has changed!", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.",
"welcome.text1": "شكرًا على تسجيلك في %1!", "reset.text2": "To continue with the password reset, please click on the following link:",
"welcome.text2": "لتفعيل حسابك، نحتاج إلى التأكد من صحة عنوان البريد الإلكتروني الذي سجلت به.", "reset.cta": "Click here to reset your password",
"welcome.text3": "تم قبول نتسجيلك ، يمكنك الدخول باتسخدام اسم المستخدم و كلمة المرور.", "digest.notifications": "You have unread notifications from %1:",
"welcome.cta": "انقر هنا لتفعيل عنوان بريدك الإلكتروني", "digest.latest_topics": "Latest topics from %1",
"invitation.text1": "%1 قام بدعوتك للانضمام لـ %2", "digest.cta": "Click here to visit %1",
"invitation.text2": "Your invitation will expire in %1 days.", "digest.unsub.info": "This digest was sent to you due to your subscription settings.",
"invitation.cta": "Click here to create your account.", "digest.daily.no_topics": "There have been no active topics in the past day",
"reset.text1": "لقد توصلنا بطلب إعادة تعيين كلمة المرور الخاصة بك، ربما لكونك قد نسيتها, إن لم يكن الأمر كذلك، المرجو تجاهل هذه الرسالة.", "notif.chat.subject": "New chat message received from %1",
"reset.text2": "لمواصلة طلب إعاة تعيين كلمة المرور، الرجاء تتبع هذا الرابط.", "notif.chat.cta": "Click here to continue the conversation",
"reset.cta": "انقر هنا لإعادة تعيين كلمة السر الخاصة بك.", "notif.chat.unsub.info": "This chat notification was sent to you due to your subscription settings.",
"reset.notify.subject": "تم تغيير كلمة المرور بنجاح", "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.",
"reset.notify.text1": "نحيطك علما أن كلمة مرورك قد تم تغييرها في %1", "unsub.cta": "Click here to alter those settings",
"reset.notify.text2": "إن لم يكن لديك علم بهذا، المرجو إشعار مدبر النظام بأسرع مايمكن.", "closing": "Thanks!"
"digest.latest_topics": "آخر المستجدات من %1",
"digest.cta": "انقر هنا لمشاهدة %1",
"digest.unsub.info": "تم إرسال هذا الإشعار بآخر المستجدات وفقا لخيارات تسجيلكم.",
"digest.day": "يوم",
"digest.week": "أسبوع",
"digest.month": "شهر",
"digest.subject": "إستهلاك ل",
"digest.title.day": "Your Daily Digest",
"digest.title.week": "Your Weekly Digest",
"digest.title.month": "Your Monthly Digest",
"notif.chat.subject": "هناك محادثة جديدة من %1",
"notif.chat.cta": "انقر هنا لمتابعة المحادثة",
"notif.chat.unsub.info": "تم إرسال هذا الإشعار بوجودة محادثة جديدة وفقا لخيارات تسجيلك.",
"notif.post.unsub.info": "تم إشعارك بهذه المشاركة بناءً على الخيارات التي سبق وأن حددتها.",
"notif.post.unsub.one-click": "Alternatively, unsubscribe from future emails like this, by clicking",
"notif.cta": "To the forum",
"notif.cta-new-reply": "View Post",
"notif.cta-new-chat": "View Chat",
"notif.test.short": "Testing Notifications",
"notif.test.long": "This is a test of the notifications email. Send help!",
"test.text1": "هذه رسالة تجريبية للتأكد من صحة إعدادت الرسائل الإلكترونية في منتدى NodeBB خاصتك.",
"unsub.cta": "انقر هنا لتغيير تلك الإعدادات",
"unsubscribe": "unsubscribe",
"unsub.success": "You will no longer receive emails from the <strong>%1</strong> mailing list",
"banned.subject": "You have been banned from %1",
"banned.text1": "The user %1 has been banned from %2.",
"banned.text2": "This ban will last until %1.",
"banned.text3": "This is the reason why you have been banned:",
"closing": "شكرًا لك!"
} }

View File

@@ -1,166 +1,64 @@
{ {
"invalid-data": "بيانات غير صحيحة", "invalid-data": "بيانات غير صالحة",
"invalid-json": "Invalid JSON",
"not-logged-in": "لم تقم بتسجيل الدخول", "not-logged-in": "لم تقم بتسجيل الدخول",
"account-locked": "تم حظر حسابك مؤقتًا.", "account-locked": "Your account has been locked temporarily",
"search-requires-login": "البحث في المنتدى يتطلب حساب - الرجاء تسجيل الدخول أو التسجيل", "search-requires-login": "Searching requires an account! Please login or register!",
"goback": "Press back to return to the previous page", "invalid-cid": "Invalid Category ID",
"invalid-cid": "قائمة غير موجودة", "invalid-tid": "Invalid Topic ID",
"invalid-tid": "موضوع غير متواجد", "invalid-pid": "Invalid Post ID",
"invalid-pid": "رد غير موجود", "invalid-uid": "Invalid User ID",
"invalid-uid": "مستخدم غير موجود",
"invalid-username": "اسم المستخدم غير مقبول", "invalid-username": "اسم المستخدم غير مقبول",
"invalid-email": "البريد الاكتروني غير مقبول", "invalid-email": "البريد الاكتروني غير مقبول",
"invalid-fullname": "Invalid Fullname", "invalid-title": "Invalid title!",
"invalid-location": "Invalid Location", "invalid-user-data": "Invalid User Data",
"invalid-birthday": "Invalid Birthday",
"invalid-title": "Invalid title",
"invalid-user-data": "بيانات المستخدم غير صحيحة",
"invalid-password": "كلمة السر غير مقبولة", "invalid-password": "كلمة السر غير مقبولة",
"invalid-login-credentials": "Invalid login credentials", "invalid-username-or-password": "Please specify both a username and password",
"invalid-username-or-password": "المرجود تحديد اسم مستخدم و كلمة مرور", "invalid-search-term": "Invalid search term",
"invalid-search-term": "كلمة البحث غير صحيحة", "invalid-pagination-value": "Invalid pagination value",
"invalid-url": "Invalid URL", "username-taken": "اسم المستخدم ماخوذ",
"local-login-disabled": "Local login system has been disabled for non-privileged accounts.", "email-taken": "البريد الالكتروني ماخوذ",
"csrf-invalid": "لم تتمكن من تسجيل الدخول. هنالك أحتمال ان جلستك انتهت. رجاءًا حاول مرة اخرى.", "email-not-confirmed": "Your email has not been confirmed yet, please click here to confirm your email.",
"invalid-pagination-value": "رقم الصفحة غير صحيح ، يجب أن يكون بين %1 و %2 .", "username-too-short": "Username too short",
"username-taken": "اسم المستخدم مأخوذ", "username-too-long": "Username too long",
"email-taken": "البريد الالكتروني مأخوذ",
"email-not-confirmed": "You are unable to post until your email is confirmed, please click here to confirm your email.",
"email-not-confirmed-chat": "لا يمكنك الدردشة حتى تقوم بتأكيد بريدك الإلكتروني، الرجاء إضغط هنا لتأكيد بريدك اﻹلكتروني.",
"email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.",
"no-email-to-confirm": "هذا المنتدى يستلزم تفعيل بريدك الإلكتروني، انقر هنا من فضلك لإدخاله.",
"email-confirm-failed": "لم نستطع تفعيل بريدك الإلكتروني، المرجو المحاولة لاحقًا.",
"confirm-email-already-sent": "لقد تم ارسال بريد التأكيد، الرجاء اﻹنتظار 1% دقائق لإعادة اﻹرسال",
"sendmail-not-found": "The sendmail executable could not be found, please ensure it is installed and executable by the user running NodeBB.",
"digest-not-enabled": "This user does not have digests enabled, or the system default is not configured to send digests",
"username-too-short": "اسم المستخدم قصير.",
"username-too-long": "اسم المستخدم طويل",
"password-too-long": "كلمة السر طويلة ",
"reset-rate-limited": "Too many password reset requests (rate limited)",
"user-banned": "المستخدم محظور", "user-banned": "المستخدم محظور",
"user-banned-reason": "Sorry, this account has been banned (Reason: %1)", "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post",
"user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)", "no-category": "Category does not exist",
"user-too-new": "عذرا, يجب أن تنتظر 1% ثواني قبل قيامك بأول مشاركة", "no-topic": "Topic does not exist",
"blacklisted-ip": "نأسف، لقد تم حظرك من استخدام وتصفح المنتدى. إذا كنت تعتقد أن هذا خطأ رجاءًا اتصل بالإدارة. ", "no-post": "Post does not exist",
"ban-expiry-missing": "رجاءًا ضع تاريخ نهاية الحظر. ", "no-group": "Group does not exist",
"no-category": "قائمة غير موجودة", "no-user": "User does not exist",
"no-topic": "موضوع غير موجود", "no-teaser": "Teaser does not exist",
"no-post": "رد غير موجود", "no-privileges": "You do not have enough privileges for this action.",
"no-group": "مجموعة غير موجودة", "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent",
"no-user": "اسم مستخدم غير موجود", "category-disabled": "Category disabled",
"no-teaser": "مقتطف غير موجود",
"no-privileges": "لاتملك الصلاحيات اللازمة للقيام بهذه العملية",
"category-disabled": "قائمة معطلة",
"topic-locked": "الموضوع مقفول", "topic-locked": "الموضوع مقفول",
"post-edit-duration-expired": "يسمح لك بتعديل مشاركتك حتى %1 ثانية من نشرها", "still-uploading": "الرجاء انتظار الرفع",
"post-edit-duration-expired-minutes": "يسمح لك بتعديل مشاركتك حتى %1 دقيقة من نشرها", "content-too-short": "Please enter a longer post. Posts should contain at least %1 characters.",
"post-edit-duration-expired-minutes-seconds": "يسمح لك بتعديل مشاركتك حتى %1 دقيقة و %2 ثوان من نشرها", "title-too-short": "Please enter a longer title. Titles should contain at least %1 characters.",
"post-edit-duration-expired-hours": "يسمح لك بتعديل مشاركتك حتى %1 ساعة من نشرها", "title-too-long": "Please enter a shorter title. Titles can't be longer than %1 characters.",
"post-edit-duration-expired-hours-minutes": "يسمح لك بتعديل مشاركتك حتى %1 ساعة و %2 دقيقة من نشرها", "too-many-posts": "You can only post once every %1 seconds - please wait before posting again",
"post-edit-duration-expired-days": "يسمح لك بتعديل مشاركتك حتى %1 يوم من نشرها", "file-too-big": "Maximum allowed file size is %1 kbs - please upload a smaller file",
"post-edit-duration-expired-days-hours": "يسمح لك بتعديل مشاركتك حتى %1 يوم و %2 ساعة من نشرها", "cant-vote-self-post": "You cannot vote for your own post",
"post-delete-duration-expired": "يسمح لك بحذف مشاركتك حتى %1 ثانية من نشرها", "already-favourited": "You have already favourited this post",
"post-delete-duration-expired-minutes": "يسمح لك بحذف مشاركتك حتى %1 دقيقة من نشرها", "already-unfavourited": "You have already unfavourited this post",
"post-delete-duration-expired-minutes-seconds": "يسمح لك بحذف مشاركتك حتى %1 دقيقة و %2 ثوان من نشرها", "cant-ban-other-admins": "You can't ban other admins!",
"post-delete-duration-expired-hours": "يسمح لك بحذف مشاركتك حتى %1 ساعة من نشرها", "invalid-image-type": "نوع الصورة ممنوع",
"post-delete-duration-expired-hours-minutes": "يسمح لك بحذف مشاركتك حتى %1 ساعة و %2 دقيقة من نشرها", "group-name-too-short": "Group name too short",
"post-delete-duration-expired-days": "يسمح لك بحذف مشاركتك حتى %1 يوم من نشرها", "group-already-exists": "Group already exists",
"post-delete-duration-expired-days-hours": "يسمح لك بحذف مشاركتك حتى %1 يوم و %2 ساعة من نشرها", "group-name-change-not-allowed": "Group name change not allowed",
"cant-delete-topic-has-reply": "لا يمكنك حذف مشاركة تم الرد عليها", "post-already-deleted": "This post has already been deleted",
"cant-delete-topic-has-replies": "لا يمكنك حذف مشاركة حصدت %1 ردود", "post-already-restored": "This post has already been restored",
"content-too-short": "يرجى ادخال موضوع أطول. على المواضيع أن تحتوي على %1 حرف على الأقل.", "topic-already-deleted": "This topic has already been deleted",
"content-too-long": "يرجى ادخال موضوع أقصر. على المواضيع أن لا تتخطى %1 حرف.", "topic-already-restored": "This topic has already been restored",
"title-too-short": "يرجى إدخال عنوان أطول. على العناوين أن تحتوي على %1 حرف على الأقل.", "topic-thumbnails-are-disabled": "Topic thumbnails are disabled.",
"title-too-long": "يرجى ادخال عنوان أقصر. على العناوين أن لا تتخطى %1 حرف.", "invalid-file": "Invalid File",
"category-not-selected": "Category not selected.", "uploads-are-disabled": "Uploads are disabled",
"too-many-posts": "يسمح لك بالنشر مرة كل %1 ثانية - يرجى الإنتظار قبل النشر مجدداً", "upload-error": "مشكلة في الرفع: 1%",
"too-many-posts-newbie": "As a new user, you can only post once every %1 second(s) until you have earned %2 reputation - please wait before posting again", "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.",
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)", "cant-chat-with-yourself": "You can't chat with yourself!",
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)", "reputation-system-disabled": "Reputation system is disabled.",
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)", "downvoting-disabled": "Downvoting is disabled",
"too-many-tags": "Too many tags. Topics can't have more than %1 tag(s)", "not-enough-reputation-to-downvote": "You do not have enough reputation to downvote this post",
"still-uploading": "الرجاء الانتظار حتى يكتمل الرفع.", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post",
"file-too-big": "الحد الأقصى لرفع الملفات %1 كيلو بت. رجاءًا ارفع ملف أصغر", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading."
"guest-upload-disabled": "خاصية رفع الملفات غير مفعلة للزوار.",
"cors-error": "Unable to upload image due to misconfigured CORS",
"already-bookmarked": "You have already bookmarked this post",
"already-unbookmarked": "You have already unbookmarked this post",
"cant-ban-other-admins": "لايمكن حظر مدبر نظام آخر.",
"cant-remove-last-admin": "رجاءًا ، أضف مدير أخر قبل حذف صلاحيات الإدارة من حسابك.",
"cant-delete-admin": "رجاءًا أزل صلاحيات الإدارة قبل حذف الحساب. ",
"invalid-image": "Invalid image",
"invalid-image-type": "نوع الصورة غير مدعوم. الأنواع المدعومة هي : %1",
"invalid-image-extension": "امتداد الصورة غير مدعوم.",
"invalid-file-type": "صيغة الملف غير مدعومة. الأنواع المدعومة هي: %1",
"invalid-image-dimensions": "Image dimensions are too big",
"group-name-too-short": "اسم المجموعة قصير",
"group-name-too-long": "اسم المجموعة طويل.",
"group-already-exists": "المجموعة موجودة مسبقا",
"group-name-change-not-allowed": "لايسمح بتغيير أسماء المجموعات",
"group-already-member": "أنت عضو في هذه المجموعة.",
"group-not-member": "أنت لست عضو في هذه المجموعة.",
"group-needs-owner": "هذه المجموعة تتطلب مالك واحد على اﻷقل",
"group-already-invited": "المستخدم سبق وأن تمت دعوته",
"group-already-requested": "سبق وتم تسجيل طلب العضوية",
"group-join-disabled": "You are not able to join this group at this time",
"group-leave-disabled": "You are not able to leave this group at this time",
"post-already-deleted": "سبق وتم حذف هذا الرد",
"post-already-restored": "سبق وتم إلغاء حذف هذا الرد",
"topic-already-deleted": "سبق وتم حذف هذا الموضوع",
"topic-already-restored": "سبق وتم إلغاء حذف هذا الرد",
"cant-purge-main-post": "لا يمكنك محو المشاركة الأساسية، يرجى حذف الموضوع بدلاً عن ذلك",
"topic-thumbnails-are-disabled": "الصور المصغرة غير مفعلة.",
"invalid-file": "ملف غير مقبول",
"uploads-are-disabled": "رفع الملفات غير مفعل",
"signature-too-long": "عذرا، توقيعك يجب ألا يتجاوز %1 حرفًا.",
"about-me-too-long": "نأسف، ( عني ) لا يمكن أن يكون أكثر من %1 حرف. ",
"cant-chat-with-yourself": "لايمكنك فتح محادثة مع نفسك",
"chat-restricted": "هذا المستخدم عطل المحادثات الواردة عليه. يجب أن يتبعك حتى تتمكن من فتح محادثة معه.",
"chat-disabled": "نظام المحادثة معطل.",
"too-many-messages": "لقد أرسلت الكثير من الرسائل، الرجاء اﻹنتظار قليلاً",
"invalid-chat-message": "الرسالة غير صالحة.",
"chat-message-too-long": "Chat messages can not be longer than %1 characters.",
"cant-edit-chat-message": "غير مصرح لك بتعديل الرسالة.",
"cant-remove-last-user": "لأيمكنك إزالت اخر مستخدم.",
"cant-delete-chat-message": "غير مصرح لك بحذف الرسالة.",
"chat-edit-duration-expired": "You are only allowed to edit chat messages for %1 second(s) after posting",
"chat-delete-duration-expired": "You are only allowed to delete chat messages for %1 second(s) after posting",
"chat-deleted-already": "This chat message has already been deleted.",
"chat-restored-already": "This chat message has already been restored.",
"already-voting-for-this-post": "لقد شاركت بالتصويت ، ألا تذكر؟",
"reputation-system-disabled": "نظام السمعة معطل",
"downvoting-disabled": "التصويتات السلبية معطلة",
"not-enough-reputation-to-downvote": "ليس لديك سمعة تكفي لإضافة صوت سلبي لهذا الموضوع",
"not-enough-reputation-to-flag": "ليس لديك سمعة تكفي للإشعار بموضوع مخل",
"not-enough-reputation-min-rep-website": "You do not have enough reputation to add a website",
"not-enough-reputation-min-rep-aboutme": "You do not have enough reputation to add an about me",
"not-enough-reputation-min-rep-signature": "You do not have enough reputation to add a signature",
"not-enough-reputation-min-rep-profile-picture": "You do not have enough reputation to add a profile picture",
"not-enough-reputation-min-rep-cover-picture": "You do not have enough reputation to add a cover picture",
"already-flagged": "لقد بلغت عن هذه المشاركة من قبل.",
"self-vote": "You cannot vote on your own post",
"reload-failed": "المنتدى واجه مشكلة أثناء إعادة التحميل: \"%1\". سيواصل المنتدى خدمة العملاء السابقين لكن يجب عليك إلغاء أي تغيير قمت به قبل إعادة التحميل.",
"registration-error": "حدث خطأ أثناء التسجيل",
"parse-error": "حدث خطأ ما أثناء تحليل استجابة الخادم",
"wrong-login-type-email": "الرجاء استعمال بريدك اﻹلكتروني للدخول",
"wrong-login-type-username": "الرجاء استعمال اسم المستخدم الخاص بك للدخول",
"sso-registration-disabled": "Registration has been disabled for %1 accounts, please register with an email address first",
"sso-multiple-association": "You cannot associate multiple accounts from this service to your NodeBB account. Please dissociate your existing account and try again.",
"invite-maximum-met": "لقد قمت بدعوة الحد الأقصى من الأشخاص (%1 من %2)",
"no-session-found": "لم دخول مسجل!",
"not-in-room": "المستخدم غير موجود في الغرفة.",
"cant-kick-self": "لا يمكنك طرد نفسك من المجموعة.",
"no-users-selected": "لا يوجد مستخدم محدد.",
"invalid-home-page-route": "Invalid home page route",
"invalid-session": "Session Mismatch",
"invalid-session-text": "يبدو أن فترة التسجيل لم تعد قائمة او هي غير مطابقة مع الخادم. يرجى إعادة تحميل هذه الصفحة.",
"no-topics-selected": "No topics selected!",
"cant-move-to-same-topic": "Can't move post to same topic!",
"cannot-block-self": "You cannot block yourself!",
"cannot-block-privileged": "You cannot block administrators or global moderators",
"cannot-block-guest": "Guest are not able to block other users",
"already-blocked": "This user is already blocked",
"already-unblocked": "This user is already unblocked",
"no-connection": "There seems to be a problem with your internet connection"
} }

View File

@@ -1,66 +0,0 @@
{
"state": "State",
"reporter": "Reporter",
"reported-at": "Reported At",
"description": "Description",
"no-flags": "Hooray! No flags found.",
"assignee": "Assignee",
"update": "Update",
"updated": "Updated",
"target-purged": "The content this flag referred to has been purged and is no longer available.",
"graph-label": "Daily Flags",
"quick-filters": "Quick Filters",
"filter-active": "There are one or more filters active in this list of flags",
"filter-reset": "Remove Filters",
"filters": "Filter Options",
"filter-reporterId": "Reporter UID",
"filter-targetUid": "Flagged UID",
"filter-type": "Flag Type",
"filter-type-all": "All Content",
"filter-type-post": "Post",
"filter-type-user": "User",
"filter-state": "State",
"filter-assignee": "Assignee UID",
"filter-cid": "Category",
"filter-quick-mine": "Assigned to me",
"filter-cid-all": "All categories",
"apply-filters": "Apply Filters",
"quick-links": "Quick Links",
"flagged-user": "Flagged User",
"view-profile": "View Profile",
"start-new-chat": "Start New Chat",
"go-to-target": "View Flag Target",
"user-view": "View Profile",
"user-edit": "Edit Profile",
"notes": "Flag Notes",
"add-note": "Add Note",
"no-notes": "No shared notes.",
"history": "Flag History",
"back": "Back to Flags List",
"no-history": "No flag history.",
"state-all": "All states",
"state-open": "New/Open",
"state-wip": "Work in Progress",
"state-resolved": "Resolved",
"state-rejected": "Rejected",
"no-assignee": "Not Assigned",
"note-added": "Note Added",
"modal-title": "Report Inappropriate Content",
"modal-body": "Please specify your reason for flagging %1 %2 for review. Alternatively, use one of the quick report buttons if applicable.",
"modal-reason-spam": "Spam",
"modal-reason-offensive": "Offensive",
"modal-reason-other": "Other (specify below)",
"modal-reason-custom": "Reason for reporting this content...",
"modal-submit": "Submit Report",
"modal-submit-success": "Content has been flagged for moderation.",
"modal-submit-confirm": "Confirm Submission",
"modal-submit-confirm-text": "You have a custom reason specified already. Are you sure you wish to submit via quick-report?",
"modal-submit-confirm-text-help": "Submitting a quick report will overwrite any custom reasons defined."
}

View File

@@ -3,113 +3,74 @@
"search": "بحث", "search": "بحث",
"buttons.close": "أغلق", "buttons.close": "أغلق",
"403.title": "غير مسموح بالدخول", "403.title": "غير مسموح بالدخول",
"403.message": "يبدو أنك قد تعثرت على صفحة لا تمتلك الصلاحية للدخول إليها", "403.message": "يبدو أنك قد تعثر على الصفحة التي لم يكن لديك الوصول إليها. ربما يجب عليك <a href='/login'> تسجيل الدخول </a> ",
"403.login": "ربما يجب عليك <a href='%1/login'>تسجل دخولك</a>.",
"404.title": "لم يتم العثور", "404.title": "لم يتم العثور",
"404.message": "الصفحة غير موجودة. العودة لـ <a href='%1/'>الرئيسية</a>", "404.message": "يبدو أنك قد تعثرت على صفحة غير موجودة. عودة إلى الصفحة الرئيسية.",
"500.title": "خطأ داخلي", "500.title": "خطأ داخلي.",
"500.message": "عفوا! يبدو وكأنه شيء ذهب على نحو خاطئ!", "500.message": "عفوا! يبدو وكأنه شيء ذهب على نحو خاطئ!",
"400.title": "طلب سيئ",
"400.message": "الرابط غير صحيح. رجاءًا تأكد من الرابط أو ارجع لـ <a href='%1/'>الرئيسية</a>",
"register": "تسجيل", "register": "تسجيل",
"login": "دخول", "login": "دخول",
"please_log_in": "الرجاء تسجيل الدخول", "please_log_in": "Please Log In",
"logout": "تسجيل الخروج", "logout": "تسجيل الخروج",
"posting_restriction_info": "إضافة مشاركات جديد حكر على الأعضاء المسجلين، انقر هنا لتسجيل الدخول.", "posting_restriction_info": "Posting is currently restricted to registered members only, click here to log in.",
"welcome_back": "مرحبًا بعودتك", "welcome_back": "Welcome Back",
"you_have_successfully_logged_in": "تم سجيل الدخول بنجاح", "you_have_successfully_logged_in": "You have successfully logged in",
"save_changes": "حفظ التغييرات", "save_changes": "حفظ التغييرات",
"save": "حفظ",
"close": "أغلق", "close": "أغلق",
"pagination": "الصفحات", "pagination": "Pagination",
"pagination.out_of": "%1 من %2", "pagination.out_of": "%1 out of %2",
"pagination.enter_index": "أدخل الرقم التسلسلي", "pagination.enter_index": "Enter index",
"header.admin": دير النظام", "header.admin": شرف",
"header.categories": "الأقسام",
"header.recent": "حديث", "header.recent": "حديث",
"header.unread": "غير مقروء", "header.unread": "غير مقروء",
"header.tags": "وسم", "header.tags": "Tags",
"header.popular": "الأكثر شهرة", "header.popular": "Popular",
"header.users": "المستخدمين", "header.users": "المستخدمين",
"header.groups": "المجموعات", "header.chats": "Chats",
"header.chats": "المحادثات", "header.notifications": "Notifications",
"header.notifications": "التنبيهات",
"header.search": "بحث", "header.search": "بحث",
"header.profile": "ملف", "header.profile": "ملف",
"header.navigation": "الاستكشاف", "notifications.loading": "تحميل التبليغات",
"notifications.loading": "تحميل التنبيهات",
"chats.loading": "تحميل الدردشات", "chats.loading": "تحميل الدردشات",
"motd.welcome": "مرحبا بكم في NodeBB، منصة المناقشة المستقبلية.", "motd.welcome": "مرحبا بكم NodeBB، منصة مناقشة المستقبل",
"previouspage": "الصفحة السابقة", "previouspage": "Previous Page",
"nextpage": "الصفحة التالية", "nextpage": "Next Page",
"alert.success": "نجاح", "alert.success": "Success",
"alert.error": "خطأ", "alert.error": "Error",
"alert.banned": "محظور", "alert.banned": "Banned",
"alert.banned.message": "لقد تم حظر حسابك. سيتم تسجيل الخروج.", "alert.banned.message": "You have just been banned, you will now be logged out.",
"alert.unfollow": "أنت لا تتابع %1 بعد الآن!", "alert.unfollow": "You are no longer following %1!",
"alert.follow": "أنت الآن تتابع %1!", "alert.follow": "You are now following %1!",
"users": "الأعضاء", "online": "Online",
"topics": "المواضيع", "users": "Users",
"posts": "المشاركات", "topics": "Topics",
"best": "الأفضل", "posts": "Posts",
"votes": "Votes", "views": "Views",
"upvoters": "الموافقين", "reputation": "Reputation",
"upvoted": "مصوت بالموجب", "read_more": "read more",
"downvoters": "مصوتين بالسالب", "posted_ago_by_guest": "posted %1 by Guest",
"downvoted": "مصوت بالسالب", "posted_ago_by": "posted %1 by %2",
"views": "المشاهدات", "posted_ago": "posted %1",
"reputation": "السمعة", "posted_in_ago_by_guest": "posted in %1 %2 by Guest",
"lastpost": "Last post", "posted_in_ago_by": "posted in %1 %2 by %3",
"firstpost": "First post", "posted_in_ago": "posted in %1 %2",
"read_more": "اقرأ المزيد", "replied_ago": "replied %1",
"more": "المزيد", "user_posted_ago": "%1 posted %2",
"posted_ago_by_guest": "كتب %1 بواسطة زائر", "guest_posted_ago": "Guest posted %1",
"posted_ago_by": "كتب %1 بواسطة %2", "last_edited_by_ago": "last edited by %1 %2",
"posted_ago": "كتب %1", "norecentposts": "No Recent Posts",
"posted_in": "كتب في %1", "norecenttopics": "No Recent Topics",
"posted_in_by": "كتب في 1% بواسطة %2", "recentposts": "Recent Posts",
"posted_in_ago": "كتب في %1 %2", "recentips": "Recently Logged In IPs",
"posted_in_ago_by": "كتب في %1 %2 من طرف %3", "away": "Away",
"user_posted_ago": "%1 كتب %2", "dnd": "Do not Disturb",
"guest_posted_ago": "كتب زائر %1", "invisible": "Invisible",
"last_edited_by": "اخر تحرير بواسطة 1%", "offline": "Offline",
"norecentposts": "لاوجود لمشاركات جديدة", "email": "Email",
"norecenttopics": "لاوجود لمواضيع جديدة", "language": "Language",
"recentposts": "آخر المشاركات", "guest": "Guest",
"recentips": "آخر عناوين ال IP التي سجلت الدخول", "guests": "Guests",
"moderator_tools": "أدوات المشرف", "updated.title": "Forum Updated",
"online": "المتواجدون حاليًّا", "updated.message": "This forum has just been updated to the latest version. Click here to refresh the page.",
"away": "غير متواجد", "privacy": "Privacy"
"dnd": "عدم الإزعاج",
"invisible": "مخفي",
"offline": "غير متصل",
"email": "عنوان البريد الإلكتروني",
"language": "اللغة",
"guest": "زائر",
"guests": "الزوار",
"former_user": "A Former User",
"updated.title": "تم تحديث المنتدى",
"updated.message": "لقد تم تحديث المنتدى إلى آخر نسخة للتو. إضغط هنا لإعادة تحميل الصفحة.",
"privacy": "الخصوصية",
"follow": "متابعة",
"unfollow": "إلغاء المتابعة",
"delete_all": "حذف الكل",
"map": "خريطة",
"sessions": "الجلسة",
"ip_address": "عنوان الآي بي",
"enter_page_number": "ادخل رقم الصفحة",
"upload_file": "ارفع ملف",
"upload": "ارفع",
"uploads": "Uploads",
"allowed-file-types": "صيغ الملفات المدعومة هي 1%",
"unsaved-changes": "لديك تغييرات لم تحفظ. هل أنت متأكد من رغبتك بمغادرة الصفحة؟",
"reconnecting-message": "يبدو أن اتصالك لـ %1 قد فقد. رجاءًا أنتظر ثم حاول الإتصال مرة اخرى.",
"play": "تشغيل",
"cookies.message": "هذا الموقع يستخدم ملفات تعريف الارتباط لضمان حصولك على أفضل تجربة على موقعنا.",
"cookies.accept": "فهمت الأمر!",
"cookies.learn_more": "أعرف المزيد",
"edited": "حُرِر",
"disabled": "معطل",
"select": "تحديد",
"user-search-prompt": "Type something here to find users..."
} }

View File

@@ -1,63 +1,8 @@
{ {
"groups": "المجموعات", "groups": "Groups",
"view_group": "معاينة المجموعة", "view_group": "View Group",
"owner": "مالك المجموعة", "details.title": "Group Details",
"new_group": "أنشئ مجموعة جديدة", "details.members": "Member List",
"no_groups_found": "لاوجدود لمجموعات يمكن معاينتها", "details.has_no_posts": "This group's members have not made any posts.",
"pending.accept": "موافق", "details.latest_posts": "Latest Posts"
"pending.reject": "رفض",
"pending.accept_all": "قبول الكل",
"pending.reject_all": "رفض الكل",
"pending.none": "لايوجد أعضاء ينتظرون التفعيل حالياً",
"invited.none": "لايوجد أعضاء مدعوون في حالياً",
"invited.uninvite": "إلغ الدعوة",
"invited.search": "ابحث عن أعضاء لدعوتهم للمجموعة",
"invited.notification_title": "You have been invited to join <strong>%1</strong>",
"request.notification_title": "Group Membership Request from <strong>%1</strong>",
"request.notification_text": "<strong>%1</strong> has requested to become a member of <strong>%2</strong>",
"cover-save": "حفظ",
"cover-saving": "جاري الحفظ",
"details.title": "تفاصيل المجموعة",
"details.members": "لائحة الأعضاء",
"details.pending": "المستخدمون في الانتظار",
"details.invited": "اﻷعضار المدعوون",
"details.has_no_posts": "أعضاء هذه المجموعة لم يضيفوا أية مشاركة",
"details.latest_posts": "آخر المشاركات",
"details.private": "خاص",
"details.disableJoinRequests": "Disable join requests",
"details.disableLeave": "Disallow users from leaving the group",
"details.grant": "منح/سحب المِلكية",
"details.kick": "طرد",
"details.kick_confirm": "Are you sure you want to remove this member from the group?",
"details.add-member": "Add Member",
"details.owner_options": "إدارة المجموعة",
"details.group_name": "اسم المجموعة",
"details.member_count": "عدد اﻷعضاء",
"details.creation_date": "تاريخ الإنشاء",
"details.description": "الوصف",
"details.badge_preview": "معاينة الوسام",
"details.change_icon": "تغيير الأيقونة",
"details.change_label_colour": "Change Label Colour",
"details.change_text_colour": "Change Text Colour",
"details.badge_text": "نص الوسام",
"details.userTitleEnabled": "إظهار الوسام",
"details.private_help": "في حالة تفعيل الخيار، الانضمام إلى المجموعة يستلزم قبول مالكها",
"details.hidden": "مخفي",
"details.hidden_help": "في حالة تفعيل الخيار، لن تظهر المجموعة للعموم والإنضمام إليها سيتلزم دعوة.",
"details.delete_group": "حذف المجموعة",
"details.private_system_help": "Private groups is disabled at system level, this option does not do anything",
"event.updated": "تم تحديث بيانات المجموعة",
"event.deleted": "تم حذف المجموعة %1",
"membership.accept-invitation": "اقبل الدعوة",
"membership.accept.notification_title": "You are now a member of <strong>%1</strong>",
"membership.invitation-pending": "الدعوة بانتظار القبول",
"membership.join-group": "انظم للمجموعة",
"membership.leave-group": "غادر المجموعة",
"membership.leave.notification_title": "<strong>%1</strong> has left group <strong>%2</strong>",
"membership.reject": "رفض",
"new-group.group_name": "اسم المجموعة",
"upload-group-cover": "Upload group cover",
"bulk-invite-instructions": "Enter a list of comma separated usernames to invite to this group",
"bulk-invite": "Bulk Invite",
"remove_group_cover_confirm": "Are you sure you want to remove the cover picture?"
} }

View File

@@ -1,12 +1,9 @@
{ {
"username-email": "اسم المستخدم / البريد الإلكتروني", "username": "Username / Email",
"username": "اسم المستخدم",
"email": "البريد الإلكتروني",
"remember_me": "تذكرني؟", "remember_me": "تذكرني؟",
"forgot_password": "نسيت كلمة المرور؟", "forgot_password": "نسيت كلمة المرور؟",
"alternative_logins": "تسجيلات الدخول البديلة", "alternative_logins": "تسجيلات الدخول البديلة",
"failed_login_attempt": "تسجيل الدخول غير ناجح", "failed_login_attempt": "فشلت محاولة تسجيل الدخول، يرجى المحاولة مرة أخرى.",
"login_successful": "قمت بتسجيل الدخول بنجاح!", "login_successful": "قمت بتسجيل الدخول بنجاح!",
"dont_have_account": "لا تملك حساب؟", "dont_have_account": "Don't have an account?"
"logged-out-due-to-inactivity": "لقد تم تسجيل خروجك من لوحة تحكم بسبب عدم نشاطك"
} }

Some files were not shown because too many files have changed in this diff Show More