Compare commits
43 Commits
v1.8.0
...
v1.1.3-aut
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
69f9c7fc41 | ||
|
|
7c16fcf0c2 | ||
|
|
7646170f84 | ||
|
|
5ec9e259f4 | ||
|
|
3fbdd2b9d2 | ||
|
|
0be97deb85 | ||
|
|
1531a4d3ea | ||
|
|
292805dacf | ||
|
|
477ec41f49 | ||
|
|
448ecb87f8 | ||
|
|
6767831074 | ||
|
|
b98a90dc4b | ||
|
|
ead5894391 | ||
|
|
a414860c7c | ||
|
|
cc3fc8e90e | ||
|
|
70103b7ce9 | ||
|
|
5d49af2f2a | ||
|
|
ae76f3cb56 | ||
|
|
4cb1e07fed | ||
|
|
b2f37dcd93 | ||
|
|
5807c1d64f | ||
|
|
0fccacbb6e | ||
|
|
900fb07577 | ||
|
|
a23d927404 | ||
|
|
8a0c28a86f | ||
|
|
6c87bc0d38 | ||
|
|
5aa83d070b | ||
|
|
13e6244d7f | ||
|
|
6e27e9a0fc | ||
|
|
7b5bedaeb4 | ||
|
|
cb5fb11ad7 | ||
|
|
1e2a82733b | ||
|
|
dd585ee4eb | ||
|
|
83ddaa3381 | ||
|
|
646a287b5c | ||
|
|
f4bc2e9cbe | ||
|
|
052cff6578 | ||
|
|
0b11461f7b | ||
|
|
e910d87e7c | ||
|
|
70d0b28337 | ||
|
|
f7715e8f27 | ||
|
|
4a561fa7d2 | ||
|
|
70128d73c7 |
@@ -5,4 +5,4 @@ languages:
|
||||
PHP: true
|
||||
exclude_paths:
|
||||
- "public/vendor/*"
|
||||
- "test/*"
|
||||
- "tests/*"
|
||||
@@ -1,20 +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
|
||||
132
.eslintrc
@@ -1,132 +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-empty": ["error", { "allowEmptyCatch": true }],
|
||||
"no-underscore-dangle": "off",
|
||||
"newline-per-chained-call": "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",
|
||||
|
||||
// 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"
|
||||
}
|
||||
}
|
||||
25
.github/CONTRIBUTING.md
vendored
@@ -1,10 +1,6 @@
|
||||
# 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.
|
||||
First of all, thank you! Please consider this [style guide](https://docs.nodebb.org/en/latest/contributing/style-guide.html) when submitting your changes. Also, please join our [community](https://community.nodebb.org) to meet other NodeBB developers and designers :)
|
||||
|
||||
## Contributor License Agreement
|
||||
|
||||
@@ -15,7 +11,7 @@ If you are writing contributions as part of employment from another company / in
|
||||
|
||||
# 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.
|
||||
Chances are somebody has run into this problem before. After consulting our [documentation](https://docs.nodebb.org/en/latest/installing/os.html), please head over to our [community support forum](https://community.nodebb.org) for advice.
|
||||
|
||||
# Found a Security Vulnerability?
|
||||
|
||||
@@ -38,9 +34,11 @@ There is a chance that the issue you are experiencing may have already been fixe
|
||||
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.
|
||||
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:
|
||||
@@ -53,13 +51,12 @@ $ 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
|
||||
$ npm ls nodebb-theme-vanilla nodebb-theme-lavender
|
||||
nodebb@0.7.0-dev /home/julian/Projects/nodebb/forum
|
||||
├── nodebb-theme-lavender@0.2.13
|
||||
└── nodebb-theme-vanilla@0.2.35
|
||||
```
|
||||
|
||||
## Attempt to use `git bisect`
|
||||
|
||||
35
.github/ISSUE_TEMPLATE.md
vendored
@@ -1,26 +1,13 @@
|
||||
<!--
|
||||
== 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/issue:
|
||||
|
||||
<!-- ++ Please include the following information when submitting a bug report ++ -->
|
||||
* NodeBB version and git hash (to find your git hash, execute `git rev-parse HEAD` from the main NodeBB directory)
|
||||
* Database (mongo or redis) and it's 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
|
||||
|
||||
- **NodeBB version:**
|
||||
- **NodeBB git hash:**
|
||||
<!-- (to find your git hash, execute `git rev-parse HEAD` from the main NodeBB directory) -->
|
||||
- **Database type:** mongo or redis
|
||||
- **Database version:**
|
||||
<!-- `mongod --version` or `redis-server --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! -->
|
||||
Thank you!
|
||||
|
||||
20
.gitignore
vendored
@@ -1,9 +1,7 @@
|
||||
yarn.lock
|
||||
npm-debug.log
|
||||
node_modules/
|
||||
sftp-config.json
|
||||
config.json
|
||||
jsconfig.json
|
||||
public/src/nodebb.min.js
|
||||
!src/views/config.json
|
||||
public/css/*.css
|
||||
@@ -17,8 +15,6 @@ provision.sh
|
||||
*.komodoproject
|
||||
.DS_Store
|
||||
feeds/recent.rss
|
||||
.eslintcache
|
||||
.svn
|
||||
|
||||
logs/
|
||||
|
||||
@@ -27,9 +23,8 @@ pidfile
|
||||
# templates
|
||||
/public/templates
|
||||
|
||||
/public/sounds
|
||||
/public/uploads
|
||||
/test/uploads
|
||||
/public/sounds
|
||||
|
||||
# compiled files
|
||||
/public/stylesheet.css
|
||||
@@ -38,9 +33,6 @@ pidfile
|
||||
/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
|
||||
@@ -58,12 +50,4 @@ tx.exe
|
||||
.transifexrc
|
||||
|
||||
##Coverage output
|
||||
coverage
|
||||
.nyc_output
|
||||
|
||||
build
|
||||
*.log
|
||||
test/files/normalise.jpg.png
|
||||
test/files/normalise-resized.jpg
|
||||
package-lock.json
|
||||
/package.json
|
||||
coverage
|
||||
@@ -74,7 +74,6 @@
|
||||
"worker" : false, // Web Workers
|
||||
"wsh" : false, // Windows Scripting Host
|
||||
"yui" : false, // Yahoo User Interface
|
||||
"mocha": true,
|
||||
|
||||
// Legacy
|
||||
"nomen" : false, // true: Prohibit dangling `_` in variables
|
||||
@@ -83,7 +82,5 @@
|
||||
"white" : false, // true: Check against strict whitespace and indentation rules
|
||||
|
||||
// Custom Globals
|
||||
"globals" : {
|
||||
"Promise": true
|
||||
} // additional predefined global variables
|
||||
"globals" : {} // additional predefined global variables
|
||||
}
|
||||
44
.travis.yml
@@ -1,37 +1,29 @@
|
||||
services:
|
||||
- mongodb
|
||||
- redis-server
|
||||
- redis-server
|
||||
before_install:
|
||||
- cp install/package.json package.json
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
||||
- "sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10"
|
||||
- "echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' | sudo tee /etc/apt/sources.list.d/mongodb.list"
|
||||
- "sudo apt-get update"
|
||||
- "sudo apt-get install mongodb-org-server"
|
||||
- npm i --production
|
||||
- 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,\"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\":27017,\"database\":0}"
|
||||
before_script:
|
||||
- sleep 15 # wait for mongodb to be ready
|
||||
- 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,\\\"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\\\":27017,\\\"database\\\":0}\"; fi"
|
||||
- sh -c "if [ '$DB' = 'redis' ]; then node app --setup=\"{\\\"url\\\":\\\"http://127.0.0.1:4567\\\",\\\"secret\\\":\\\"abcdef\\\",\\\"database\\\":\\\"redis\\\",\\\"mongo:host\\\":\\\"127.0.0.1\\\",\\\"mongo:port\\\":27017,\\\"mongo:username\\\":\\\"\\\",\\\"mongo:password\\\":\\\"\\\",\\\"mongo:database\\\":0,\\\"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}\"; fi"
|
||||
after_success:
|
||||
- "npm run coveralls"
|
||||
- "until nc -z localhost 27017; do echo Waiting for MongoDB; sleep 1; done"
|
||||
language: node_js
|
||||
sudo: false
|
||||
dist: trusty
|
||||
env:
|
||||
- CXX=g++-4.8 DB=mongodb
|
||||
- CXX=g++-4.8 DB=redis
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
- mongodb-3.4-trusty
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
||||
- mongodb-org-server
|
||||
- g++-4.8
|
||||
node_js:
|
||||
- "9"
|
||||
- "8"
|
||||
- "7"
|
||||
- "6"
|
||||
- "4.2"
|
||||
- "4.1"
|
||||
- "4.0"
|
||||
- "0.11"
|
||||
- "0.10"
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- develop
|
||||
only:
|
||||
- master
|
||||
2648
.tx/config
21
Dockerfile
@@ -1,20 +1,15 @@
|
||||
# The base image is the latest 8.x node (LTS)
|
||||
FROM node:8.9.0
|
||||
|
||||
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 && npm cache clean --force
|
||||
COPY . /usr/src/app
|
||||
# The base image is the latest 4.x node (LTS) on jessie (debian)
|
||||
# -onbuild will install the node dependencies found in the project package.json
|
||||
# and copy its content in /usr/src/app, its WORKDIR
|
||||
FROM node:4-onbuild
|
||||
|
||||
ENV NODE_ENV=production \
|
||||
daemon=false \
|
||||
silent=false
|
||||
|
||||
CMD ./nodebb start
|
||||
# nodebb setup will ask you for connection information to a redis (default), mongodb then run the forum
|
||||
# nodebb upgrade is not included and might be desired
|
||||
CMD node app --setup && npm start
|
||||
|
||||
# the default port for NodeBB is exposed outside the container
|
||||
EXPOSE 4567
|
||||
EXPOSE 4567
|
||||
|
||||
169
Gruntfile.js
@@ -1,70 +1,56 @@
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
var fork = require('child_process').fork;
|
||||
var env = process.env;
|
||||
var worker;
|
||||
var updateWorker;
|
||||
var initWorker;
|
||||
var incomplete = [];
|
||||
var running = 0;
|
||||
var fork = require('child_process').fork,
|
||||
env = process.env,
|
||||
worker,
|
||||
incomplete = [];
|
||||
|
||||
env.NODE_ENV = env.NODE_ENV || 'development';
|
||||
|
||||
module.exports = function (grunt) {
|
||||
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();
|
||||
|
||||
var updateArgs = args.slice(),
|
||||
fromFile = '',
|
||||
compiling = '',
|
||||
time = Date.now();
|
||||
|
||||
if (target === 'lessUpdated_Client') {
|
||||
compiling = 'clientCSS';
|
||||
fromFile = ['js', 'tpl', 'acpLess'];
|
||||
compiling = 'clientLess';
|
||||
} else if (target === 'lessUpdated_Admin') {
|
||||
compiling = 'acpCSS';
|
||||
fromFile = ['js', 'tpl', 'clientLess'];
|
||||
compiling = 'acpLess';
|
||||
} else if (target === 'clientUpdated') {
|
||||
fromFile = ['clientLess', 'acpLess', 'tpl'];
|
||||
compiling = 'js';
|
||||
} else if (target === 'templatesUpdated') {
|
||||
fromFile = ['js', 'clientLess', 'acpLess'];
|
||||
compiling = 'tpl';
|
||||
} else if (target === 'langUpdated') {
|
||||
compiling = 'lang';
|
||||
} else if (target === 'serverUpdated') {
|
||||
// Do nothing, just restart
|
||||
fromFile = ['clientLess', 'acpLess', 'js', 'tpl'];
|
||||
}
|
||||
|
||||
if (compiling && incomplete.indexOf(compiling) === -1) {
|
||||
incomplete.push(compiling);
|
||||
}
|
||||
fromFile = fromFile.filter(function(ext) {
|
||||
return incomplete.indexOf(ext) === -1;
|
||||
});
|
||||
|
||||
updateArgs.push('--build');
|
||||
updateArgs.push(incomplete.join(','));
|
||||
updateArgs.push('--from-file=' + fromFile.join(','));
|
||||
incomplete.push(compiling);
|
||||
|
||||
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 = [];
|
||||
worker = fork('app.js', updateArgs, { env: env });
|
||||
|
||||
if (grunt.option('verbose')) {
|
||||
grunt.log.writeln('NodeBB restarted in ' + (Date.now() - time) + ' ms');
|
||||
}
|
||||
}
|
||||
});
|
||||
worker.on('message', function() {
|
||||
if (incomplete.length) {
|
||||
incomplete = [];
|
||||
|
||||
if (grunt.option('verbose')) {
|
||||
grunt.log.writeln('NodeBB restarted in ' + (Date.now() - time) + ' ms');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -73,94 +59,49 @@ module.exports = function (grunt) {
|
||||
watch: {
|
||||
lessUpdated_Client: {
|
||||
files: [
|
||||
'public/less/*.less',
|
||||
'!public/less/admin/**/*.less',
|
||||
'node_modules/nodebb-*/**/*.less',
|
||||
'public/*.less',
|
||||
'node_modules/nodebb-*/*.less', 'node_modules/nodebb-*/**/*.less',
|
||||
'!node_modules/nodebb-*/node_modules/**',
|
||||
'!node_modules/nodebb-*/.git/**',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
'!node_modules/nodebb-*/.git/**'
|
||||
]
|
||||
},
|
||||
lessUpdated_Admin: {
|
||||
files: [
|
||||
'public/less/admin/**/*.less',
|
||||
'node_modules/nodebb-*/**/*.less',
|
||||
'!node_modules/nodebb-*/node_modules/**',
|
||||
'!node_modules/nodebb-*/.git/**',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
files: ['public/**/*.less']
|
||||
},
|
||||
clientUpdated: {
|
||||
files: [
|
||||
'public/src/**/*.js',
|
||||
'node_modules/nodebb-*/**/*.js',
|
||||
'node_modules/nodebb-*/*.js', 'node_modules/nodebb-*/**/*.js',
|
||||
'!node_modules/nodebb-*/node_modules/**',
|
||||
'node_modules/benchpressjs/build/benchpress.js',
|
||||
'!node_modules/nodebb-*/.git/**',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
'node_modules/templates.js/lib/templates.js',
|
||||
'!node_modules/nodebb-*/.git/**'
|
||||
]
|
||||
},
|
||||
serverUpdated: {
|
||||
files: ['*.js', 'install/*.js', 'src/**/*.js'],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
files: ['*.js', 'install/*.js', 'src/**/*.js']
|
||||
},
|
||||
templatesUpdated: {
|
||||
files: [
|
||||
'src/views/**/*.tpl',
|
||||
'node_modules/nodebb-*/**/*.tpl',
|
||||
'node_modules/nodebb-*/*.tpl', 'node_modules/nodebb-*/**/*.tpl',
|
||||
'!node_modules/nodebb-*/node_modules/**',
|
||||
'!node_modules/nodebb-*/.git/**',
|
||||
],
|
||||
options: {
|
||||
interval: 1000,
|
||||
},
|
||||
},
|
||||
langUpdated: {
|
||||
files: [
|
||||
'public/language/en-GB/*.json',
|
||||
'public/language/en-GB/**/*.json',
|
||||
'node_modules/nodebb-*/**/*.json',
|
||||
'!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,
|
||||
},
|
||||
},
|
||||
},
|
||||
'!node_modules/nodebb-*/.git/**'
|
||||
]
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||
|
||||
grunt.registerTask('default', ['watch']);
|
||||
if (grunt.option('skip')) {
|
||||
grunt.registerTask('default', ['watch:serverUpdated']);
|
||||
} else {
|
||||
grunt.registerTask('default', ['watch']);
|
||||
}
|
||||
|
||||
|
||||
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,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
worker = fork('app.js', args, { env: env });
|
||||
grunt.event.on('watch', update);
|
||||
};
|
||||
};
|
||||
17
README.md
@@ -2,8 +2,7 @@
|
||||
|
||||
[](https://gitter.im/NodeBB/NodeBB?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](https://travis-ci.org/NodeBB/NodeBB)
|
||||
[](https://coveralls.io/github/NodeBB/NodeBB?branch=master)
|
||||
[](https://david-dm.org/nodebb/nodebb?path=install)
|
||||
[](https://david-dm.org/nodebb/nodebb)
|
||||
[](https://codeclimate.com/github/NodeBB/NodeBB)
|
||||
[](https://readthedocs.org/projects/nodebb/?badge=latest)
|
||||
|
||||
@@ -11,18 +10,17 @@
|
||||
|
||||
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)
|
||||
* [Documentation & Installation Instructions](http://docs.nodebb.org)
|
||||
* [Help translate NodeBB](https://www.transifex.com/projects/p/nodebb/)
|
||||
* [NodeBB Blog](http://blog.nodebb.org)
|
||||
* [Premium Hosting for NodeBB](http://www.nodebb.org/ "NodeBB")
|
||||
* [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")
|
||||
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
|
||||
|
||||
## Screenshots
|
||||
|
||||
NodeBB's theming engine is highly flexible and does not restrict your design choices. Check out some themed installs in these screenshots below:
|
||||
|
||||
[](http://i.imgur.com/VCoOFyq.png)
|
||||
[](http://i.imgur.com/FLOUuIq.png)
|
||||
[](http://i.imgur.com/Ud1LrfI.png)
|
||||
@@ -32,8 +30,6 @@ NodeBB's theming engine is highly flexible and does not restrict your design cho
|
||||
[](http://i.imgur.com/LmHtPho.png)
|
||||
[](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/II1byYs.png)
|
||||
|
||||
@@ -41,6 +37,7 @@ Our minimalist "Persona" theme gets you going right away, no coding experience r
|
||||
|
||||
## 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 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.
|
||||
@@ -50,13 +47,13 @@ Our minimalist "Persona" theme gets you going right away, no coding experience r
|
||||
|
||||
NodeBB requires the following software to be installed:
|
||||
|
||||
* A version of Node.js at least 6 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
|
||||
* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB)
|
||||
|
||||
## 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
|
||||
|
||||
@@ -71,7 +68,7 @@ It is important to ensure that your NodeBB and database servers are secured. Bea
|
||||
|
||||
## 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
|
||||
|
||||
|
||||
329
app.js
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
NodeBB - A better forum platform for the modern web
|
||||
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
|
||||
it under the terms of the GNU General Public License as published by
|
||||
@@ -17,29 +17,35 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
if (require.main !== module) {
|
||||
require.main.require = function (path) {
|
||||
return require(path);
|
||||
};
|
||||
}
|
||||
"use strict";
|
||||
/*global require, global, process*/
|
||||
|
||||
var nconf = require('nconf');
|
||||
nconf.argv().env({
|
||||
separator: '__',
|
||||
});
|
||||
nconf.argv().env('__');
|
||||
|
||||
var async = require('async');
|
||||
var winston = require('winston');
|
||||
var path = require('path');
|
||||
|
||||
var file = require('./src/file');
|
||||
var url = require('url'),
|
||||
async = require('async'),
|
||||
winston = require('winston'),
|
||||
colors = require('colors'),
|
||||
path = require('path'),
|
||||
pkg = require('./package.json'),
|
||||
file = require('./src/file');
|
||||
|
||||
global.env = process.env.NODE_ENV || 'production';
|
||||
|
||||
winston.remove(winston.transports.Console);
|
||||
winston.add(winston.transports.Console, {
|
||||
colorize: true,
|
||||
timestamp: function() {
|
||||
var date = new Date();
|
||||
return date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0,5) + ' [' + global.process.pid + ']';
|
||||
},
|
||||
level: nconf.get('log-level') || (global.env === 'production' ? 'info' : 'verbose')
|
||||
});
|
||||
|
||||
|
||||
// Alternate configuration file support
|
||||
var configFile = path.join(__dirname, 'config.json');
|
||||
var configFile = path.join(__dirname, '/config.json');
|
||||
|
||||
if (nconf.get('config')) {
|
||||
configFile = path.resolve(__dirname, nconf.get('config'));
|
||||
@@ -47,53 +53,278 @@ if (nconf.get('config')) {
|
||||
|
||||
var configExists = file.existsSync(configFile) || (nconf.get('url') && nconf.get('secret') && nconf.get('database'));
|
||||
|
||||
var prestart = require('./src/prestart');
|
||||
prestart.loadConfig(configFile);
|
||||
prestart.versionCheck();
|
||||
prestart.setupWinston();
|
||||
loadConfig();
|
||||
|
||||
if (!process.send) {
|
||||
// If run using `node app`, log GNU copyright info along with server info
|
||||
winston.info('NodeBB v' + nconf.get('version') + ' Copyright (C) 2013-' + (new Date()).getFullYear() + ' NodeBB Inc.');
|
||||
winston.info('NodeBB v' + nconf.get('version') + ' Copyright (C) 2013-2014 NodeBB Inc.');
|
||||
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('');
|
||||
}
|
||||
|
||||
|
||||
if (nconf.get('setup') || nconf.get('install')) {
|
||||
require('./src/cli/setup').setup();
|
||||
setup();
|
||||
} else if (!configExists) {
|
||||
require('./install/web').install(nconf.get('port'));
|
||||
} else if (nconf.get('upgrade')) {
|
||||
require('./src/cli/upgrade').upgrade(true);
|
||||
upgrade();
|
||||
} else if (nconf.get('reset')) {
|
||||
var options = {
|
||||
theme: nconf.get('t'),
|
||||
plugin: nconf.get('p'),
|
||||
widgets: nconf.get('w'),
|
||||
settings: nconf.get('s'),
|
||||
all: nconf.get('a'),
|
||||
};
|
||||
|
||||
async.series([
|
||||
async.apply(require('./src/cli/reset').reset, options),
|
||||
require('./src/meta/build').buildAll,
|
||||
], function (err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
});
|
||||
require('./src/reset').reset();
|
||||
} else if (nconf.get('activate')) {
|
||||
require('./src/cli/manage').activate(nconf.get('activate'));
|
||||
activate();
|
||||
} else if (nconf.get('plugins')) {
|
||||
require('./src/cli/manage').listPlugins();
|
||||
} else if (nconf.get('build')) {
|
||||
require('./src/meta/build').build(nconf.get('build'));
|
||||
} else if (nconf.get('events')) {
|
||||
require('./src/cli/manage').listEvents();
|
||||
listPlugins();
|
||||
} else {
|
||||
require('./src/start').start();
|
||||
start();
|
||||
}
|
||||
|
||||
function loadConfig() {
|
||||
winston.verbose('* using configuration stored in: %s', configFile);
|
||||
|
||||
nconf.file({
|
||||
file: configFile
|
||||
});
|
||||
|
||||
nconf.defaults({
|
||||
base_dir: __dirname,
|
||||
themes_path: path.join(__dirname, 'node_modules'),
|
||||
views_dir: path.join(__dirname, 'public/templates'),
|
||||
version: pkg.version
|
||||
});
|
||||
|
||||
if (!nconf.get('isCluster')) {
|
||||
nconf.set('isPrimary', 'true');
|
||||
nconf.set('isCluster', 'false');
|
||||
}
|
||||
|
||||
// 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-persona/templates'));
|
||||
}
|
||||
|
||||
|
||||
function start() {
|
||||
var db = require('./src/database');
|
||||
|
||||
// nconf defaults, if not set in config
|
||||
if (!nconf.get('upload_path')) {
|
||||
nconf.set('upload_path', '/public/uploads');
|
||||
}
|
||||
// Parse out the relative_url and other goodies from the configured URL
|
||||
var urlObject = url.parse(nconf.get('url'));
|
||||
var relativePath = urlObject.pathname !== '/' ? urlObject.pathname : '';
|
||||
nconf.set('base_url', urlObject.protocol + '//' + urlObject.host);
|
||||
nconf.set('secure', urlObject.protocol === 'https:');
|
||||
nconf.set('use_port', !!urlObject.port);
|
||||
nconf.set('relative_path', relativePath);
|
||||
nconf.set('port', urlObject.port || nconf.get('port') || nconf.get('PORT') || 4567);
|
||||
nconf.set('upload_url', nconf.get('upload_path').replace(/^\/public/, ''));
|
||||
|
||||
if (nconf.get('isPrimary') === 'true') {
|
||||
winston.info('Time: %s', (new Date()).toString());
|
||||
winston.info('Initializing NodeBB v%s', nconf.get('version'));
|
||||
|
||||
|
||||
var host = nconf.get(nconf.get('database') + ':host'),
|
||||
storeLocation = host ? 'at ' + host + (host.indexOf('/') === -1 ? ':' + nconf.get(nconf.get('database') + ':port') : '') : '';
|
||||
|
||||
winston.verbose('* using %s store %s', nconf.get('database'), storeLocation);
|
||||
winston.verbose('* using themes stored in: %s', nconf.get('themes_path'));
|
||||
}
|
||||
|
||||
process.on('SIGTERM', shutdown);
|
||||
process.on('SIGINT', shutdown);
|
||||
process.on('SIGHUP', restart);
|
||||
process.on('message', function(message) {
|
||||
if (typeof message !== 'object') {
|
||||
return;
|
||||
}
|
||||
var meta = require('./src/meta');
|
||||
var emitter = require('./src/emitter');
|
||||
switch (message.action) {
|
||||
case 'reload':
|
||||
meta.reload();
|
||||
break;
|
||||
case 'js-propagate':
|
||||
meta.js.target = message.data;
|
||||
emitter.emit('meta:js.compiled');
|
||||
winston.verbose('[cluster] Client-side javascript and mapping propagated to worker %s', process.pid);
|
||||
break;
|
||||
case 'css-propagate':
|
||||
meta.css.cache = message.cache;
|
||||
meta.css.acpCache = message.acpCache;
|
||||
emitter.emit('meta:css.compiled');
|
||||
winston.verbose('[cluster] Stylesheets propagated to worker %s', process.pid);
|
||||
break;
|
||||
case 'templates:compiled':
|
||||
emitter.emit('templates:compiled');
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
process.on('uncaughtException', function(err) {
|
||||
winston.error(err.stack);
|
||||
console.log(err.stack);
|
||||
|
||||
require('./src/meta').js.killMinifier();
|
||||
shutdown(1);
|
||||
});
|
||||
|
||||
async.waterfall([
|
||||
async.apply(db.init),
|
||||
async.apply(db.checkCompatibility),
|
||||
function(next) {
|
||||
require('./src/meta').configs.init(next);
|
||||
},
|
||||
function(next) {
|
||||
if (nconf.get('dep-check') === undefined || nconf.get('dep-check') !== false) {
|
||||
require('./src/meta').dependencies.check(next);
|
||||
} else {
|
||||
winston.warn('[init] Dependency checking skipped!');
|
||||
setImmediate(next);
|
||||
}
|
||||
},
|
||||
function(next) {
|
||||
require('./src/upgrade').check(next);
|
||||
},
|
||||
function(next) {
|
||||
var webserver = require('./src/webserver');
|
||||
require('./src/socket.io').init(webserver.server);
|
||||
|
||||
if (nconf.get('isPrimary') === 'true' && !nconf.get('jobsDisabled')) {
|
||||
require('./src/notifications').init();
|
||||
require('./src/user').startJobs();
|
||||
}
|
||||
|
||||
webserver.listen();
|
||||
}
|
||||
], function(err) {
|
||||
if (err) {
|
||||
switch(err.message) {
|
||||
case 'schema-out-of-date':
|
||||
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');
|
||||
break;
|
||||
case 'dependencies-out-of-date':
|
||||
winston.warn('One or more of NodeBB\'s dependent packages are out-of-date. Please run the following command to update them:');
|
||||
winston.warn(' ./nodebb upgrade');
|
||||
break;
|
||||
case 'dependencies-missing':
|
||||
winston.warn('One or more of NodeBB\'s dependent packages are missing. Please run the following command to update them:');
|
||||
winston.warn(' ./nodebb upgrade');
|
||||
break;
|
||||
default:
|
||||
if (err.stacktrace !== false) {
|
||||
winston.error(err.stack);
|
||||
} else {
|
||||
winston.error(err.message);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Either way, bad stuff happened. Abort start.
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function setup() {
|
||||
winston.info('NodeBB Setup Triggered via Command Line');
|
||||
|
||||
var install = require('./src/install');
|
||||
|
||||
process.stdout.write('\nWelcome to NodeBB!\n');
|
||||
process.stdout.write('\nThis looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.\n');
|
||||
process.stdout.write('Press enter to accept the default setting (shown in brackets).\n');
|
||||
|
||||
install.setup(function (err, data) {
|
||||
var separator = ' ';
|
||||
if (process.stdout.columns > 10) {
|
||||
for(var x=0,cols=process.stdout.columns-10;x<cols;x++) {
|
||||
separator += '=';
|
||||
}
|
||||
}
|
||||
process.stdout.write('\n' + separator + '\n\n');
|
||||
|
||||
if (err) {
|
||||
winston.error('There was a problem completing NodeBB setup: ', err.message);
|
||||
} else {
|
||||
if (data.hasOwnProperty('password')) {
|
||||
process.stdout.write('An administrative user was automatically created for you:\n');
|
||||
process.stdout.write(' Username: ' + data.username + '\n');
|
||||
process.stdout.write(' Password: ' + data.password + '\n');
|
||||
process.stdout.write('\n');
|
||||
}
|
||||
process.stdout.write('NodeBB Setup Completed. Run \'./nodebb start\' to manually start your NodeBB server.\n');
|
||||
|
||||
// If I am a child process, notify the parent of the returned data before exiting (useful for notifying
|
||||
// hosts of auto-generated username/password during headless setups)
|
||||
if (process.send) {
|
||||
process.send(data);
|
||||
}
|
||||
}
|
||||
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
|
||||
function upgrade() {
|
||||
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 activate() {
|
||||
require('./src/database').init(function(err) {
|
||||
var plugin = nconf.get('_')[1] ? nconf.get('_')[1] : nconf.get('activate'),
|
||||
db = require('./src/database');
|
||||
|
||||
winston.info('Activating plugin %s', plugin);
|
||||
|
||||
db.sortedSetAdd('plugins:active', 0, plugin, start);
|
||||
});
|
||||
}
|
||||
|
||||
function listPlugins() {
|
||||
require('./src/database').init(function(err) {
|
||||
var db = require('./src/database');
|
||||
|
||||
db.getSortedSetRange('plugins:active', 0, -1, function(err, plugins) {
|
||||
winston.info('Active plugins: \n\t - ' + plugins.join('\n\t - '));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var bcrypt = require('bcryptjs');
|
||||
var async = require('async');
|
||||
var bcrypt = require('bcryptjs'),
|
||||
async = require('async');
|
||||
|
||||
|
||||
process.on('message', function (msg) {
|
||||
process.on('message', function(msg) {
|
||||
if (msg.type === 'hash') {
|
||||
hashPassword(msg.password, msg.rounds);
|
||||
} else if (msg.type === 'compare') {
|
||||
bcrypt.compare(String(msg.password || ''), String(msg.hash || ''), done);
|
||||
bcrypt.compare(msg.password, msg.hash, done);
|
||||
}
|
||||
});
|
||||
|
||||
function hashPassword(password, rounds) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
function(next) {
|
||||
bcrypt.genSalt(parseInt(rounds, 10), next);
|
||||
},
|
||||
function (salt, next) {
|
||||
function(salt, next) {
|
||||
bcrypt.hash(password, salt, next);
|
||||
},
|
||||
}
|
||||
], done);
|
||||
}
|
||||
|
||||
function done(err, result) {
|
||||
if (err) {
|
||||
process.send({ err: err.message });
|
||||
process.send({err: err.message});
|
||||
return process.disconnect();
|
||||
}
|
||||
process.send({ result: result });
|
||||
process.send({result: result});
|
||||
process.disconnect();
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
"allowLocalLogin": 1,
|
||||
"allowAccountDelete": 1,
|
||||
"allowFileUploads": 0,
|
||||
"allowedFileExtensions": "png,jpg,bmp",
|
||||
"allowUserHomePage": 1,
|
||||
"maximumFileSize": 2048,
|
||||
"minimumTitleLength": 3,
|
||||
@@ -27,23 +26,14 @@
|
||||
"minimumPasswordLength": 6,
|
||||
"maximumSignatureLength": 255,
|
||||
"maximumAboutMeLength": 1000,
|
||||
"maximumChatMessageLength": 1000,
|
||||
"maximumProfileImageSize": 256,
|
||||
"maximumCoverImageSize": 2048,
|
||||
"profileImageDimension": 200,
|
||||
"profileImageDimension": 128,
|
||||
"requireEmailConfirmation": 0,
|
||||
"allowProfileImageUploads": 1,
|
||||
"teaserPost": "last-reply",
|
||||
"allowPrivateGroups": 1,
|
||||
"unreadCutoff": 2,
|
||||
"bookmarkThreshold": 5,
|
||||
"topicsPerList": 20,
|
||||
"autoDetectLang": 1,
|
||||
"min:rep:flag": 0,
|
||||
"notificationType_upvote": "notification",
|
||||
"notificationType_new-topic": "notification",
|
||||
"notificationType_new-reply": "notification",
|
||||
"notificationType_follow": "notification",
|
||||
"notificationType_new-chat": "notification",
|
||||
"notificationType_group-invite": "notification"
|
||||
"topicsPerList": 20
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{
|
||||
"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>",
|
||||
"html": "<footer id=\"footer\" class=\"container footer\">\r\n\t<div class=\"copyright\">\r\n\t\tCopyright © 2016 <a target=\"_blank\" href=\"https://nodebb.org\">NodeBB Forums</a> | <a target=\"_blank\" href=\"//github.com/NodeBB/NodeBB/graphs/contributors\">Contributors</a>\r\n\t</div>\r\n</footer>",
|
||||
"title":"",
|
||||
"container":""
|
||||
}
|
||||
|
||||
@@ -1,74 +1,85 @@
|
||||
[
|
||||
{
|
||||
"route": "/categories",
|
||||
"title": "[[global:header.categories]]",
|
||||
"title": "\\[\\[global:header.categories\\]\\]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-list",
|
||||
"textClass": "visible-xs-inline",
|
||||
"text": "[[global:header.categories]]"
|
||||
"text": "\\[\\[global:header.categories\\]\\]"
|
||||
},
|
||||
{
|
||||
"id": "unread-count",
|
||||
"route": "/unread",
|
||||
"title": "[[global:header.unread]]",
|
||||
"title": "\\[\\[global:header.unread\\]\\]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-inbox",
|
||||
"textClass": "visible-xs-inline",
|
||||
"text": "[[global:header.unread]]",
|
||||
"text": "\\[\\[global:header.unread\\]\\]",
|
||||
"properties": {
|
||||
"loggedIn": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"route": "/recent",
|
||||
"title": "[[global:header.recent]]",
|
||||
"title": "\\[\\[global:header.recent\\]\\]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-clock-o",
|
||||
"textClass": "visible-xs-inline",
|
||||
"text": "[[global:header.recent]]"
|
||||
"text": "\\[\\[global:header.recent\\]\\]"
|
||||
},
|
||||
{
|
||||
"route": "/tags",
|
||||
"title": "[[global:header.tags]]",
|
||||
"title": "\\[\\[global:header.tags\\]\\]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-tags",
|
||||
"textClass": "visible-xs-inline",
|
||||
"text": "[[global:header.tags]]"
|
||||
"text": "\\[\\[global:header.tags\\]\\]"
|
||||
},
|
||||
{
|
||||
"route": "/popular",
|
||||
"title": "[[global:header.popular]]",
|
||||
"title": "\\[\\[global:header.popular\\]\\]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-fire",
|
||||
"textClass": "visible-xs-inline",
|
||||
"text": "[[global:header.popular]]"
|
||||
"text": "\\[\\[global:header.popular\\]\\]"
|
||||
},
|
||||
{
|
||||
"route": "/users",
|
||||
"title": "[[global:header.users]]",
|
||||
"title": "\\[\\[global:header.users\\]\\]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-user",
|
||||
"textClass": "visible-xs-inline",
|
||||
"text": "[[global:header.users]]"
|
||||
"text": "\\[\\[global:header.users\\]\\]"
|
||||
},
|
||||
{
|
||||
"route": "/groups",
|
||||
"title": "[[global:header.groups]]",
|
||||
"title": "\\[\\[global:header.groups\\]\\]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-group",
|
||||
"textClass": "visible-xs-inline",
|
||||
"text": "[[global:header.groups]]"
|
||||
"text": "\\[\\[global:header.groups\\]\\]"
|
||||
},
|
||||
{
|
||||
"route": "/admin",
|
||||
"title": "[[global:header.admin]]",
|
||||
"title": "\\[\\[global:header.admin\\]\\]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-cogs",
|
||||
"textClass": "visible-xs-inline",
|
||||
"text": "[[global:header.admin]]",
|
||||
"text": "\\[\\[global:header.admin\\]\\]",
|
||||
"properties": {
|
||||
"targetBlank": false,
|
||||
"adminOnly": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"route": "/search",
|
||||
"title": "\\[\\[global:header.search\\]\\]",
|
||||
"enabled": true,
|
||||
"iconClass": "fa-search",
|
||||
"textClass": "visible-xs-inline",
|
||||
"text": "\\[\\[global:header.search\\]\\]",
|
||||
"properties": {
|
||||
"searchInstalled": true
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -1,4 +1,4 @@
|
||||
'use strict';
|
||||
"use strict";
|
||||
|
||||
var async = require('async');
|
||||
var prompt = require('prompt');
|
||||
@@ -6,18 +6,19 @@ var winston = require('winston');
|
||||
|
||||
var questions = {
|
||||
redis: require('../src/database/redis').questions,
|
||||
mongo: require('../src/database/mongo').questions,
|
||||
mongo: require('../src/database/mongo').questions
|
||||
};
|
||||
|
||||
module.exports = function (config, callback) {
|
||||
module.exports = function(config, callback) {
|
||||
async.waterfall([
|
||||
function (next) {
|
||||
winston.info('\nNow configuring ' + config.database + ' database:');
|
||||
process.stdout.write('\n');
|
||||
winston.info('Now configuring ' + config.database + ' database:');
|
||||
getDatabaseConfig(config, next);
|
||||
},
|
||||
function (databaseConfig, next) {
|
||||
saveDatabaseConfig(config, databaseConfig, next);
|
||||
},
|
||||
}
|
||||
], callback);
|
||||
};
|
||||
|
||||
@@ -33,7 +34,7 @@ function getDatabaseConfig(config, callback) {
|
||||
prompt.get(questions.redis, callback);
|
||||
}
|
||||
} else if (config.database === 'mongo') {
|
||||
if ((config['mongo:host'] && config['mongo:port']) || config['mongo:uri']) {
|
||||
if (config['mongo:host'] && config['mongo:port']) {
|
||||
callback(null, config);
|
||||
} else {
|
||||
prompt.get(questions.mongo, callback);
|
||||
@@ -54,7 +55,7 @@ function saveDatabaseConfig(config, databaseConfig, callback) {
|
||||
host: databaseConfig['redis:host'],
|
||||
port: databaseConfig['redis:port'],
|
||||
password: databaseConfig['redis:password'],
|
||||
database: databaseConfig['redis:database'],
|
||||
database: databaseConfig['redis:database']
|
||||
};
|
||||
|
||||
if (config.redis.host.slice(0, 1) === '/') {
|
||||
@@ -66,17 +67,16 @@ function saveDatabaseConfig(config, databaseConfig, callback) {
|
||||
port: databaseConfig['mongo:port'],
|
||||
username: databaseConfig['mongo:username'],
|
||||
password: databaseConfig['mongo:password'],
|
||||
database: databaseConfig['mongo:database'],
|
||||
uri: databaseConfig['mongo:uri'],
|
||||
database: databaseConfig['mongo:database']
|
||||
};
|
||||
} else {
|
||||
return callback(new Error('unknown database : ' + config.database));
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
callback(null, config);
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
{
|
||||
"name": "nodebb",
|
||||
"license": "GPL-3.0",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "1.8.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"
|
||||
},
|
||||
"dependencies": {
|
||||
"ace-builds": "^1.2.9",
|
||||
"async": "2.6.0",
|
||||
"autoprefixer": "7.2.4",
|
||||
"bcryptjs": "2.4.3",
|
||||
"benchpressjs": "^1.2.1",
|
||||
"body-parser": "^1.18.2",
|
||||
"bootstrap": "^3.3.7",
|
||||
"chart.js": "^2.7.1",
|
||||
"clipboard": "1.7.1",
|
||||
"colors": "^1.1.2",
|
||||
"compression": "^1.7.1",
|
||||
"commander": "^2.12.2",
|
||||
"connect-ensure-login": "^0.1.1",
|
||||
"connect-flash": "^0.1.1",
|
||||
"connect-mongo": "2.0.1",
|
||||
"connect-multiparty": "^2.1.0",
|
||||
"connect-redis": "3.3.3",
|
||||
"cookie-parser": "^1.4.3",
|
||||
"cron": "^1.3.0",
|
||||
"cropperjs": "^1.2.2",
|
||||
"csurf": "^1.9.0",
|
||||
"daemon": "^1.1.0",
|
||||
"diff": "^3.4.0",
|
||||
"express": "^4.16.2",
|
||||
"express-session": "^1.15.6",
|
||||
"express-useragent": "1.0.8",
|
||||
"graceful-fs": "^4.1.11",
|
||||
"helmet": "^3.11.0",
|
||||
"html-to-text": "3.3.0",
|
||||
"ipaddr.js": "^1.5.4",
|
||||
"jimp": "0.2.28",
|
||||
"jquery": "^3.2.1",
|
||||
"jsesc": "2.5.1",
|
||||
"json-2-csv": "^2.1.2",
|
||||
"less": "^2.7.3",
|
||||
"lodash": "^4.17.4",
|
||||
"logrotate-stream": "^0.2.5",
|
||||
"lru-cache": "4.1.1",
|
||||
"material-design-lite": "^1.3.0",
|
||||
"mime": "^2.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mongodb": "2.2.33",
|
||||
"morgan": "^1.9.0",
|
||||
"mousetrap": "^1.6.1",
|
||||
"mubsub": "^1.4.0",
|
||||
"nconf": "^0.9.1",
|
||||
"nodebb-plugin-composer-default": "6.0.16",
|
||||
"nodebb-plugin-dbsearch": "2.0.9",
|
||||
"nodebb-plugin-emoji": "^2.1.0",
|
||||
"nodebb-plugin-emoji-android": "2.0.0",
|
||||
"nodebb-plugin-markdown": "8.4.2",
|
||||
"nodebb-plugin-mentions": "2.2.3",
|
||||
"nodebb-plugin-soundpack-default": "1.0.0",
|
||||
"nodebb-plugin-spam-be-gone": "0.5.3",
|
||||
"nodebb-rewards-essentials": "0.0.11",
|
||||
"nodebb-theme-lavender": "5.0.4",
|
||||
"nodebb-theme-persona": "8.0.2",
|
||||
"nodebb-theme-slick": "1.1.5",
|
||||
"nodebb-theme-vanilla": "9.0.0",
|
||||
"nodebb-widget-essentials": "4.0.2",
|
||||
"nodemailer": "4.4.1",
|
||||
"passport": "^0.4.0",
|
||||
"passport-local": "1.0.0",
|
||||
"postcss": "6.0.15",
|
||||
"postcss-clean": "1.1.0",
|
||||
"promise-polyfill": "^7.0.0",
|
||||
"prompt": "^1.0.0",
|
||||
"redis": "2.8.0",
|
||||
"request": "2.83.0",
|
||||
"rimraf": "2.6.2",
|
||||
"rss": "^1.2.2",
|
||||
"sanitize-html": "^1.16.3",
|
||||
"semver": "^5.4.1",
|
||||
"serve-favicon": "^2.4.5",
|
||||
"sitemap": "^1.13.0",
|
||||
"socket.io": "2.0.4",
|
||||
"socket.io-adapter-mongo": "^2.0.1",
|
||||
"socket.io-client": "2.0.4",
|
||||
"socket.io-redis": "5.2.0",
|
||||
"socketio-wildcard": "2.0.0",
|
||||
"spdx-license-list": "^3.0.1",
|
||||
"spider-detector": "1.0.18",
|
||||
"toobusy-js": "^0.5.1",
|
||||
"uglify-js": "^3.3.4",
|
||||
"validator": "9.2.0",
|
||||
"winston": "^2.4.0",
|
||||
"xml": "^1.0.1",
|
||||
"xregexp": "3.2.0",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"coveralls": "^3.0.0",
|
||||
"eslint": "^4.14.0",
|
||||
"eslint-config-airbnb-base": "^12.1.0",
|
||||
"eslint-plugin-import": "^2.8.0",
|
||||
"grunt": "^1.0.1",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"jsdom": "^11.5.1",
|
||||
"mocha": "^4.1.0",
|
||||
"mocha-lcov-reporter": "^1.3.0",
|
||||
"nyc": "^11.4.1",
|
||||
"smtp-server": "^3.4.1"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/NodeBB/NodeBB/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
160
install/web.js
@@ -1,65 +1,48 @@
|
||||
'use strict';
|
||||
"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-js');
|
||||
var nconf = require('nconf');
|
||||
var _ = require('lodash');
|
||||
var Benchpress = require('benchpressjs');
|
||||
|
||||
var app = express();
|
||||
var server;
|
||||
|
||||
winston.add(winston.transports.File, {
|
||||
filename: 'logs/webinstall.log',
|
||||
colorize: true,
|
||||
timestamp: function () {
|
||||
timestamp: function() {
|
||||
var date = new Date();
|
||||
return date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0, 5) + ' [' + global.process.pid + ']';
|
||||
return date.getDate() + '/' + (date.getMonth() + 1) + ' ' + date.toTimeString().substr(0,5) + ' [' + global.process.pid + ']';
|
||||
},
|
||||
level: 'verbose',
|
||||
level: 'verbose'
|
||||
});
|
||||
|
||||
var web = {};
|
||||
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',
|
||||
'public/src/installer/install.js'
|
||||
];
|
||||
|
||||
web.install = function (port) {
|
||||
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.engine('tpl', require('templates.js').__express);
|
||||
app.set('view engine', 'tpl');
|
||||
app.set('views', path.join(__dirname, '../src/views'));
|
||||
app.use(bodyParser.urlencoded({
|
||||
extended: true,
|
||||
extended: true
|
||||
}));
|
||||
|
||||
async.parallel([compileLess, compileJS, copyCSS], function (err) {
|
||||
if (err) {
|
||||
winston.error(err);
|
||||
}
|
||||
async.parallel([compileLess, compileJS], function() {
|
||||
setupRoutes();
|
||||
launchExpress(port);
|
||||
});
|
||||
@@ -67,7 +50,7 @@ web.install = function (port) {
|
||||
|
||||
|
||||
function launchExpress(port) {
|
||||
server = app.listen(port, function () {
|
||||
server = app.listen(port, function() {
|
||||
winston.info('Web installer listening on http://%s:%s', '0.0.0.0', port);
|
||||
});
|
||||
}
|
||||
@@ -76,24 +59,14 @@ 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'];
|
||||
var databases = dbs.map(function (databaseName) {
|
||||
var questions = require('../src/database/' + databaseName).questions.filter(function (question) {
|
||||
return question && !question.hideOnWebInstall;
|
||||
});
|
||||
|
||||
var databases = dbs.map(function(el) {
|
||||
return {
|
||||
name: databaseName,
|
||||
questions: questions,
|
||||
name: el,
|
||||
questions: require('../src/database/' + el).questions
|
||||
};
|
||||
});
|
||||
|
||||
@@ -102,27 +75,25 @@ function welcome(req, res) {
|
||||
res.render('install/index', {
|
||||
databases: databases,
|
||||
skipDatabaseSetup: !!nconf.get('database'),
|
||||
error: !!res.locals.error,
|
||||
success: !!res.locals.success,
|
||||
error: res.locals.error ? true : false,
|
||||
success: res.locals.success ? true : false,
|
||||
values: req.body,
|
||||
minimumPasswordLength: defaults.minimumPasswordLength,
|
||||
minimumPasswordLength: defaults.minimumPasswordLength
|
||||
});
|
||||
}
|
||||
|
||||
function install(req, res) {
|
||||
req.setTimeout(0);
|
||||
var setupEnvVars = _.assign({}, process.env);
|
||||
for (var i in req.body) {
|
||||
if (req.body.hasOwnProperty(i) && !process.env.hasOwnProperty(i)) {
|
||||
setupEnvVars[i.replace(':', '__')] = req.body[i];
|
||||
process.env[i.replace(':', '__')] = req.body[i];
|
||||
}
|
||||
}
|
||||
|
||||
var child = require('child_process').fork('app', ['--setup'], {
|
||||
env: setupEnvVars,
|
||||
env: process.env
|
||||
});
|
||||
|
||||
child.on('close', function (data) {
|
||||
child.on('close', function(data) {
|
||||
if (data === 0) {
|
||||
res.locals.success = true;
|
||||
} else {
|
||||
@@ -137,88 +108,51 @@ function launch(req, res) {
|
||||
res.json({});
|
||||
server.close();
|
||||
|
||||
var child = childProcess.spawn('node', ['loader.js'], {
|
||||
var child = require('child_process').spawn('node', ['loader.js'], {
|
||||
detached: true,
|
||||
stdio: ['ignore', 'ignore', 'ignore'],
|
||||
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');
|
||||
process.stdout.write('\nStarting NodeBB\n');
|
||||
process.stdout.write(' "./nodebb stop" to stop the NodeBB server\n');
|
||||
process.stdout.write(' "./nodebb log" to view server output\n');
|
||||
process.stdout.write(' "./nodebb restart" to restart NodeBB\n');
|
||||
|
||||
var filesToDelete = [
|
||||
'installer.css',
|
||||
'installer.min.js',
|
||||
'bootstrap.min.css',
|
||||
];
|
||||
child.unref();
|
||||
process.exit(0);
|
||||
|
||||
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);
|
||||
}
|
||||
if ((nconf.get('from-file') || '').indexOf('less') !== -1) {
|
||||
winston.info('LESS compilation skipped');
|
||||
return callback(false);
|
||||
}
|
||||
|
||||
less.render(style.toString(), function (err, css) {
|
||||
if (err) {
|
||||
fs.readFile(path.join(__dirname, '../public/less/install.less'), function(err, style) {
|
||||
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);
|
||||
fs.writeFile(path.join(__dirname, '../public/stylesheet.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);
|
||||
}
|
||||
if ((nconf.get('from-file') || '').indexOf('js') !== -1) {
|
||||
winston.info('Client-side JS compilation skipped');
|
||||
return callback(false);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
var scriptPath = path.join(__dirname, '..');
|
||||
var result = uglify.minify(scripts.map(function(script) {
|
||||
return path.join(scriptPath, script);
|
||||
}));
|
||||
|
||||
|
||||
fs.writeFile(path.join(__dirname, '../public/nodebb.min.js'), result.code, callback);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
module.exports = web;
|
||||
module.exports = web;
|
||||
206
loader.js
@@ -1,34 +1,41 @@
|
||||
'use strict';
|
||||
|
||||
var nconf = require('nconf');
|
||||
var fs = require('fs');
|
||||
var url = require('url');
|
||||
var path = require('path');
|
||||
var fork = require('child_process').fork;
|
||||
var async = require('async');
|
||||
var logrotate = require('logrotate-stream');
|
||||
var nconf = require('nconf'),
|
||||
fs = require('fs'),
|
||||
url = require('url'),
|
||||
path = require('path'),
|
||||
fork = require('child_process').fork,
|
||||
|
||||
var file = require('./src/file');
|
||||
var pkg = require('./package.json');
|
||||
async = require('async'),
|
||||
logrotate = require('logrotate-stream'),
|
||||
file = require('./src/file'),
|
||||
pkg = require('./package.json');
|
||||
|
||||
nconf.argv().env().file({
|
||||
file: path.join(__dirname, 'config.json'),
|
||||
file: path.join(__dirname, '/config.json')
|
||||
});
|
||||
|
||||
var pidFilePath = path.join(__dirname, 'pidfile');
|
||||
var outputLogFilePath = path.join(__dirname, 'logs/output.log');
|
||||
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,
|
||||
};
|
||||
var appPath = path.join(__dirname, 'app.js');
|
||||
var pidFilePath = __dirname + '/pidfile',
|
||||
output = logrotate({ file: __dirname + '/logs/output.log', size: '1m', keep: 3, compress: true }),
|
||||
silent = nconf.get('silent') === 'false' ? false : nconf.get('silent') !== false,
|
||||
numProcs,
|
||||
workers = [],
|
||||
|
||||
Loader.init = function (callback) {
|
||||
Loader = {
|
||||
timesStarted: 0,
|
||||
js: {
|
||||
target: {}
|
||||
},
|
||||
css: {
|
||||
cache: undefined,
|
||||
acpCache: undefined
|
||||
},
|
||||
templatesCompiled: false
|
||||
};
|
||||
|
||||
Loader.init = function(callback) {
|
||||
if (silent) {
|
||||
console.log = function () {
|
||||
console.log = function() {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
output.write(args.join(' ') + '\n');
|
||||
};
|
||||
@@ -40,7 +47,7 @@ Loader.init = function (callback) {
|
||||
callback();
|
||||
};
|
||||
|
||||
Loader.displayStartupMessages = function (callback) {
|
||||
Loader.displayStartupMessages = function(callback) {
|
||||
console.log('');
|
||||
console.log('NodeBB v' + pkg.version + ' Copyright (C) 2013-2014 NodeBB Inc.');
|
||||
console.log('This program comes with ABSOLUTELY NO WARRANTY.');
|
||||
@@ -50,24 +57,25 @@ Loader.displayStartupMessages = function (callback) {
|
||||
callback();
|
||||
};
|
||||
|
||||
Loader.addWorkerEvents = function (worker) {
|
||||
worker.on('exit', function (code, signal) {
|
||||
Loader.addWorkerEvents = function(worker) {
|
||||
|
||||
worker.on('exit', function(code, signal) {
|
||||
if (code !== 0) {
|
||||
if (Loader.timesStarted < numProcs * 3) {
|
||||
Loader.timesStarted += 1;
|
||||
if (Loader.timesStarted < numProcs*3) {
|
||||
Loader.timesStarted++;
|
||||
if (Loader.crashTimer) {
|
||||
clearTimeout(Loader.crashTimer);
|
||||
}
|
||||
Loader.crashTimer = setTimeout(function () {
|
||||
Loader.crashTimer = setTimeout(function() {
|
||||
Loader.timesStarted = 0;
|
||||
}, 10000);
|
||||
} 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();
|
||||
}
|
||||
}
|
||||
|
||||
console.log('[cluster] Child Process (' + worker.pid + ') has exited (code: ' + code + ', signal: ' + signal + ')');
|
||||
console.log('[cluster] Child Process (' + worker.pid + ') has exited (code: ' + code + ', signal: ' + signal +')');
|
||||
if (!(worker.suicide || code === 0)) {
|
||||
console.log('[cluster] Spinning up another process...');
|
||||
|
||||
@@ -75,27 +83,76 @@ Loader.addWorkerEvents = function (worker) {
|
||||
}
|
||||
});
|
||||
|
||||
worker.on('message', function (message) {
|
||||
worker.on('message', function(message) {
|
||||
if (message && typeof message === 'object' && message.action) {
|
||||
switch (message.action) {
|
||||
case 'restart':
|
||||
console.log('[cluster] Restarting...');
|
||||
Loader.restart();
|
||||
case 'ready':
|
||||
if (Loader.js.target['nodebb.min.js'] && Loader.js.target['acp.min.js'] && !worker.isPrimary) {
|
||||
worker.send({
|
||||
action: 'js-propagate',
|
||||
data: Loader.js.target
|
||||
});
|
||||
}
|
||||
|
||||
if (Loader.css.cache && !worker.isPrimary) {
|
||||
worker.send({
|
||||
action: 'css-propagate',
|
||||
cache: Loader.css.cache,
|
||||
acpCache: Loader.css.acpCache
|
||||
});
|
||||
}
|
||||
|
||||
if (Loader.templatesCompiled && !worker.isPrimary) {
|
||||
worker.send({
|
||||
action: 'templates:compiled'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
case 'reload':
|
||||
console.log('[cluster] Reloading...');
|
||||
Loader.reload();
|
||||
case 'restart':
|
||||
console.log('[cluster] Restarting...');
|
||||
Loader.restart();
|
||||
break;
|
||||
case 'reload':
|
||||
console.log('[cluster] Reloading...');
|
||||
Loader.reload();
|
||||
break;
|
||||
case 'js-propagate':
|
||||
Loader.js.target = message.data;
|
||||
|
||||
Loader.notifyWorkers({
|
||||
action: 'js-propagate',
|
||||
data: message.data
|
||||
}, worker.pid);
|
||||
break;
|
||||
case 'css-propagate':
|
||||
Loader.css.cache = message.cache;
|
||||
Loader.css.acpCache = message.acpCache;
|
||||
|
||||
Loader.notifyWorkers({
|
||||
action: 'css-propagate',
|
||||
cache: message.cache,
|
||||
acpCache: message.acpCache
|
||||
}, worker.pid);
|
||||
break;
|
||||
case 'templates:compiled':
|
||||
Loader.templatesCompiled = true;
|
||||
|
||||
Loader.notifyWorkers({
|
||||
action: 'templates:compiled',
|
||||
}, worker.pid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Loader.start = function (callback) {
|
||||
Loader.start = function(callback) {
|
||||
numProcs = getPorts().length;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -106,19 +163,18 @@ Loader.start = function (callback) {
|
||||
|
||||
function forkWorker(index, isPrimary) {
|
||||
var ports = getPorts();
|
||||
var args = [];
|
||||
|
||||
if (!ports[index]) {
|
||||
if(!ports[index]) {
|
||||
return console.log('[cluster] invalid port for worker : ' + index + ' ports: ' + ports.length);
|
||||
}
|
||||
|
||||
process.env.isPrimary = isPrimary;
|
||||
process.env.isCluster = ports.length > 1;
|
||||
process.env.isCluster = ports.length > 1 ? true : false;
|
||||
process.env.port = ports[index];
|
||||
|
||||
var worker = fork(appPath, args, {
|
||||
var worker = fork('app.js', [], {
|
||||
silent: silent,
|
||||
env: process.env,
|
||||
env: process.env
|
||||
});
|
||||
|
||||
worker.index = index;
|
||||
@@ -129,7 +185,7 @@ function forkWorker(index, isPrimary) {
|
||||
Loader.addWorkerEvents(worker);
|
||||
|
||||
if (silent) {
|
||||
var output = logrotate({ file: outputLogFilePath, size: '1m', keep: 3, compress: true });
|
||||
var output = logrotate({ file: __dirname + '/logs/output.log', size: '1m', keep: 3, compress: true });
|
||||
worker.stdout.pipe(output);
|
||||
worker.stderr.pipe(output);
|
||||
}
|
||||
@@ -142,66 +198,44 @@ function getPorts() {
|
||||
process.exit();
|
||||
}
|
||||
var urlObject = url.parse(_url);
|
||||
var port = nconf.get('PORT') || nconf.get('port') || urlObject.port || 4567;
|
||||
var port = nconf.get('port') || nconf.get('PORT') || urlObject.port || 4567;
|
||||
if (!Array.isArray(port)) {
|
||||
port = [port];
|
||||
}
|
||||
return port;
|
||||
}
|
||||
|
||||
Loader.restart = function () {
|
||||
Loader.restart = function() {
|
||||
killWorkers();
|
||||
|
||||
var pathToConfig = path.join(__dirname, '/config.json');
|
||||
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 () {
|
||||
workers.forEach(function (worker) {
|
||||
Loader.reload = function() {
|
||||
workers.forEach(function(worker) {
|
||||
worker.send({
|
||||
action: 'reload',
|
||||
action: 'reload'
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Loader.stop = function () {
|
||||
Loader.stop = function() {
|
||||
killWorkers();
|
||||
|
||||
// Clean up the pidfile
|
||||
if (nconf.get('daemon') !== 'false' && nconf.get('daemon') !== false) {
|
||||
fs.unlinkSync(pidFilePath);
|
||||
}
|
||||
fs.unlinkSync(__dirname + '/pidfile');
|
||||
};
|
||||
|
||||
function killWorkers() {
|
||||
workers.forEach(function (worker) {
|
||||
workers.forEach(function(worker) {
|
||||
worker.suicide = true;
|
||||
worker.kill();
|
||||
});
|
||||
}
|
||||
|
||||
Loader.notifyWorkers = function (msg, worker_pid) {
|
||||
Loader.notifyWorkers = function(msg, worker_pid) {
|
||||
worker_pid = parseInt(worker_pid, 10);
|
||||
workers.forEach(function (worker) {
|
||||
workers.forEach(function(worker) {
|
||||
if (parseInt(worker.pid, 10) !== worker_pid) {
|
||||
try {
|
||||
worker.send(msg);
|
||||
@@ -212,7 +246,7 @@ Loader.notifyWorkers = function (msg, worker_pid) {
|
||||
});
|
||||
};
|
||||
|
||||
fs.open(path.join(__dirname, 'config.json'), 'r', function (err) {
|
||||
fs.open(path.join(__dirname, 'config.json'), 'r', function(err) {
|
||||
if (!err) {
|
||||
if (nconf.get('daemon') !== 'false' && nconf.get('daemon') !== false) {
|
||||
if (file.existsSync(pidFilePath)) {
|
||||
@@ -227,25 +261,23 @@ fs.open(path.join(__dirname, 'config.json'), 'r', function (err) {
|
||||
|
||||
require('daemon')({
|
||||
stdout: process.stdout,
|
||||
stderr: process.stderr,
|
||||
cwd: process.cwd(),
|
||||
stderr: process.stderr
|
||||
});
|
||||
|
||||
fs.writeFileSync(pidFilePath, process.pid);
|
||||
fs.writeFile(__dirname + '/pidfile', process.pid);
|
||||
}
|
||||
|
||||
async.series([
|
||||
Loader.init,
|
||||
Loader.displayStartupMessages,
|
||||
Loader.start,
|
||||
], function (err) {
|
||||
Loader.start
|
||||
], function(err) {
|
||||
if (err) {
|
||||
console.error('[loader] Error during startup');
|
||||
throw err;
|
||||
console.log('[loader] Error during startup: ' + err.message);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// No config detected, kickstart web installer
|
||||
fork('app');
|
||||
var child = require('child_process').fork('app');
|
||||
}
|
||||
});
|
||||
|
||||
81
minifier.js
Normal file
@@ -0,0 +1,81 @@
|
||||
"use strict";
|
||||
|
||||
var uglifyjs = require('uglify-js');
|
||||
var async = require('async');
|
||||
var fs = require('fs');
|
||||
var file = require('./src/file');
|
||||
|
||||
var Minifier = {
|
||||
js: {}
|
||||
};
|
||||
|
||||
/* Javascript */
|
||||
Minifier.js.minify = function (scripts, minify, callback) {
|
||||
|
||||
scripts = scripts.filter(function(file) {
|
||||
return file && file.endsWith('.js');
|
||||
});
|
||||
|
||||
async.filter(scripts, function(script, next) {
|
||||
file.exists(script, function(exists) {
|
||||
if (!exists) {
|
||||
console.warn('[minifier] file not found, ' + script);
|
||||
}
|
||||
next(exists);
|
||||
});
|
||||
}, function(scripts) {
|
||||
if (minify) {
|
||||
minifyScripts(scripts, callback);
|
||||
} else {
|
||||
concatenateScripts(scripts, callback);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
process.on('message', function(payload) {
|
||||
switch(payload.action) {
|
||||
case 'js':
|
||||
Minifier.js.minify(payload.scripts, payload.minify, function(minified/*, sourceMap*/) {
|
||||
process.send({
|
||||
type: 'end',
|
||||
// sourceMap: sourceMap,
|
||||
minified: minified
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
function minifyScripts(scripts, callback) {
|
||||
// The portions of code involving the source map are commented out as they're broken in UglifyJS2
|
||||
// Follow along here: https://github.com/mishoo/UglifyJS2/issues/700
|
||||
try {
|
||||
var minified = uglifyjs.minify(scripts, {
|
||||
// outSourceMap: "nodebb.min.js.map",
|
||||
compress: false
|
||||
});
|
||||
|
||||
callback(minified.code/*, minified.map*/);
|
||||
} catch(err) {
|
||||
process.send({
|
||||
type: 'error',
|
||||
message: err.message
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function concatenateScripts(scripts, callback) {
|
||||
async.map(scripts, fs.readFile, function(err, scripts) {
|
||||
if (err) {
|
||||
process.send({
|
||||
type: 'error',
|
||||
message: err.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
scripts = scripts.join(require('os').EOL + ';');
|
||||
|
||||
callback(scripts);
|
||||
});
|
||||
}
|
||||
386
nodebb
@@ -1,5 +1,387 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
try {
|
||||
var colors = require('colors'),
|
||||
cproc = require('child_process'),
|
||||
argv = require('minimist')(process.argv.slice(2)),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
request = require('request'),
|
||||
semver = require('semver'),
|
||||
prompt = require('prompt'),
|
||||
async = require('async');
|
||||
} catch (e) {
|
||||
if (e.code === 'MODULE_NOT_FOUND') {
|
||||
process.stdout.write('NodeBB could not be started because it\'s dependencies have not been installed.\n');
|
||||
process.stdout.write('Please ensure that you have executed "npm install --production" prior to running NodeBB.\n\n');
|
||||
process.stdout.write('For more information, please see: https://docs.nodebb.org/en/latest/installing/os.html\n\n');
|
||||
process.stdout.write('Could not start: ' + e.code + '\n');
|
||||
|
||||
require('./src/cli');
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
var getRunningPid = function(callback) {
|
||||
fs.readFile(__dirname + '/pidfile', {
|
||||
encoding: 'utf-8'
|
||||
}, function(err, pid) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
try {
|
||||
process.kill(parseInt(pid, 10), 0);
|
||||
callback(null, parseInt(pid, 10));
|
||||
} catch(e) {
|
||||
callback(e);
|
||||
}
|
||||
});
|
||||
},
|
||||
getCurrentVersion = function(callback) {
|
||||
fs.readFile(path.join(__dirname, 'package.json'), { encoding: 'utf-8' }, function(err, pkg) {
|
||||
try {
|
||||
pkg = JSON.parse(pkg);
|
||||
return callback(null, pkg.version);
|
||||
} catch(err) {
|
||||
return callback(err);
|
||||
}
|
||||
})
|
||||
},
|
||||
fork = function (args) {
|
||||
cproc.fork('app.js', args, {
|
||||
cwd: __dirname,
|
||||
silent: false
|
||||
});
|
||||
},
|
||||
getInstalledPlugins = function(callback) {
|
||||
async.parallel({
|
||||
files: async.apply(fs.readdir, path.join(__dirname, 'node_modules')),
|
||||
deps: async.apply(fs.readFile, path.join(__dirname, 'package.json'), { encoding: 'utf-8' })
|
||||
}, function(err, payload) {
|
||||
var isNbbModule = /^nodebb-(?:plugin|theme|widget|rewards)-[\w\-]+$/,
|
||||
moduleName, isGitRepo;
|
||||
|
||||
payload.files = payload.files.filter(function(file) {
|
||||
return isNbbModule.test(file);
|
||||
});
|
||||
|
||||
try {
|
||||
payload.deps = JSON.parse(payload.deps).dependencies;
|
||||
payload.bundled = [];
|
||||
payload.installed = [];
|
||||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
for (moduleName in payload.deps) {
|
||||
if (isNbbModule.test(moduleName)) {
|
||||
payload.bundled.push(moduleName);
|
||||
}
|
||||
}
|
||||
|
||||
// Whittle down deps to send back only extraneously installed plugins/themes/etc
|
||||
payload.files.forEach(function(moduleName) {
|
||||
try {
|
||||
fs.accessSync(path.join(__dirname, 'node_modules/' + moduleName, '.git'));
|
||||
isGitRepo = true;
|
||||
} catch(e) {
|
||||
isGitRepo = false;
|
||||
}
|
||||
|
||||
if (
|
||||
payload.files.indexOf(moduleName) !== -1 // found in `node_modules/`
|
||||
&& payload.bundled.indexOf(moduleName) === -1 // not found in `package.json`
|
||||
&& !fs.lstatSync(path.join(__dirname, 'node_modules/' + moduleName)).isSymbolicLink() // is not a symlink
|
||||
&& !isGitRepo // .git/ does not exist, so it is not a git repository
|
||||
) {
|
||||
payload.installed.push(moduleName);
|
||||
}
|
||||
});
|
||||
|
||||
getModuleVersions(payload.installed, callback);
|
||||
});
|
||||
},
|
||||
getModuleVersions = function(modules, callback) {
|
||||
var versionHash = {};
|
||||
|
||||
async.eachLimit(modules, 50, function(module, next) {
|
||||
fs.readFile(path.join(__dirname, 'node_modules/' + module + '/package.json'), { encoding: 'utf-8' }, function(err, pkg) {
|
||||
try {
|
||||
pkg = JSON.parse(pkg);
|
||||
versionHash[module] = pkg.version;
|
||||
next();
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
}, function(err) {
|
||||
callback(err, versionHash);
|
||||
});
|
||||
},
|
||||
checkPlugins = function(standalone, callback) {
|
||||
if (standalone) {
|
||||
process.stdout.write('Checking installed plugins and themes for updates... ');
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
async.apply(async.parallel, {
|
||||
plugins: async.apply(getInstalledPlugins),
|
||||
version: async.apply(getCurrentVersion)
|
||||
}),
|
||||
function(payload, next) {
|
||||
var toCheck = Object.keys(payload.plugins);
|
||||
|
||||
if (!toCheck.length) {
|
||||
process.stdout.write('OK'.green + '\n'.reset);
|
||||
return next(null, []); // no extraneous plugins installed
|
||||
}
|
||||
|
||||
request({
|
||||
method: 'GET',
|
||||
url: 'https://packages.nodebb.org/api/v1/suggest?version=' + payload.version + '&package[]=' + toCheck.join('&package[]='),
|
||||
json: true
|
||||
}, function(err, res, body) {
|
||||
if (err) {
|
||||
process.stdout.write('error'.red + '\n'.reset);
|
||||
return next(err);
|
||||
}
|
||||
process.stdout.write('OK'.green + '\n'.reset);
|
||||
|
||||
if (!Array.isArray(body) && toCheck.length === 1) {
|
||||
body = [body];
|
||||
}
|
||||
|
||||
var current, suggested,
|
||||
upgradable = body.map(function(suggestObj) {
|
||||
current = payload.plugins[suggestObj.package];
|
||||
suggested = suggestObj.version;
|
||||
|
||||
if (suggestObj.code === 'match-found' && semver.gt(suggested, current)) {
|
||||
return {
|
||||
name: suggestObj.package,
|
||||
current: current,
|
||||
suggested: suggested
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}).filter(Boolean);
|
||||
|
||||
next(null, upgradable);
|
||||
})
|
||||
}
|
||||
], callback);
|
||||
},
|
||||
upgradePlugins = function(callback) {
|
||||
var standalone = false;
|
||||
if (typeof callback !== 'function') {
|
||||
callback = function() {};
|
||||
standalone = true;
|
||||
};
|
||||
|
||||
checkPlugins(standalone, function(err, found) {
|
||||
if (err) {
|
||||
process.stdout.write('\Warning'.yellow + ': An unexpected error occured when attempting to verify plugin upgradability\n'.reset);
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (found && found.length) {
|
||||
process.stdout.write('\nA total of ' + new String(found.length).bold + ' package(s) can be upgraded:\n');
|
||||
found.forEach(function(suggestObj) {
|
||||
process.stdout.write(' * '.yellow + suggestObj.name.reset + ' (' + suggestObj.current.yellow + ' -> '.reset + suggestObj.suggested.green + ')\n'.reset);
|
||||
});
|
||||
process.stdout.write('\n');
|
||||
} else {
|
||||
if (standalone) {
|
||||
process.stdout.write('\nAll packages up-to-date!'.green + '\n'.reset);
|
||||
}
|
||||
return callback();
|
||||
}
|
||||
|
||||
prompt.message = '';
|
||||
prompt.delimiter = '';
|
||||
|
||||
prompt.start();
|
||||
prompt.get({
|
||||
name: 'upgrade',
|
||||
description: 'Proceed with upgrade (y|n)?'.reset,
|
||||
type: 'string'
|
||||
}, function(err, result) {
|
||||
if (['y', 'Y', 'yes', 'YES'].indexOf(result.upgrade) !== -1) {
|
||||
process.stdout.write('\nUpgrading packages...');
|
||||
var args = ['npm', 'i'];
|
||||
found.forEach(function(suggestObj) {
|
||||
args.push(suggestObj.name + '@' + suggestObj.suggested);
|
||||
});
|
||||
|
||||
require('child_process').execFile('/usr/bin/env', args, { stdio: 'ignore' }, function(err) {
|
||||
if (!err) {
|
||||
process.stdout.write(' OK\n'.green);
|
||||
}
|
||||
|
||||
callback(err);
|
||||
});
|
||||
} else {
|
||||
process.stdout.write('\nPackage upgrades skipped'.yellow + '. Check for upgrades at any time by running "'.reset + './nodebb upgrade-plugins'.green + '".\n'.reset);
|
||||
callback();
|
||||
}
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
switch(process.argv[2]) {
|
||||
case 'status':
|
||||
getRunningPid(function(err, pid) {
|
||||
if (!err) {
|
||||
process.stdout.write('\nNodeBB Running '.bold + '(pid '.cyan + pid.toString().cyan + ')\n'.cyan);
|
||||
process.stdout.write('\t"' + './nodebb stop'.yellow + '" to stop the NodeBB server\n');
|
||||
process.stdout.write('\t"' + './nodebb log'.yellow + '" to view server output\n');
|
||||
process.stdout.write('\t"' + './nodebb restart'.yellow + '" to restart NodeBB\n\n');
|
||||
} else {
|
||||
process.stdout.write('\nNodeBB is not running\n'.bold);
|
||||
process.stdout.write('\t"' + './nodebb start'.yellow + '" to launch the NodeBB server\n\n'.reset);
|
||||
}
|
||||
})
|
||||
break;
|
||||
|
||||
case 'start':
|
||||
process.stdout.write('\nStarting NodeBB\n'.bold);
|
||||
process.stdout.write(' "' + './nodebb stop'.yellow + '" to stop the NodeBB server\n');
|
||||
process.stdout.write(' "' + './nodebb log'.yellow + '" to view server output\n');
|
||||
process.stdout.write(' "' + './nodebb restart'.yellow + '" to restart NodeBB\n\n'.reset);
|
||||
|
||||
// Spawn a new NodeBB process
|
||||
cproc.fork(__dirname + '/loader.js', {
|
||||
env: process.env
|
||||
});
|
||||
break;
|
||||
|
||||
case 'stop':
|
||||
getRunningPid(function(err, pid) {
|
||||
if (!err) {
|
||||
process.kill(pid, 'SIGTERM');
|
||||
process.stdout.write('Stopping NodeBB. Goodbye!\n')
|
||||
} else {
|
||||
process.stdout.write('NodeBB is already stopped.\n');
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'restart':
|
||||
getRunningPid(function(err, pid) {
|
||||
if (!err) {
|
||||
process.kill(pid, 'SIGHUP');
|
||||
process.stdout.write('\nRestarting NodeBB\n'.bold);
|
||||
} else {
|
||||
process.stdout.write('NodeBB could not be restarted, as a running instance could not be found.\n');
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'reload':
|
||||
getRunningPid(function(err, pid) {
|
||||
if (!err) {
|
||||
process.kill(pid, 'SIGUSR2');
|
||||
} else {
|
||||
process.stdout.write('NodeBB could not be reloaded, as a running instance could not be found.\n');
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
case 'dev':
|
||||
process.env.NODE_ENV = 'development';
|
||||
cproc.fork(__dirname + '/loader.js', ['--no-daemon', '--no-silent'], {
|
||||
env: process.env
|
||||
});
|
||||
break;
|
||||
|
||||
case 'log':
|
||||
process.stdout.write('\nType '.red + 'Ctrl-C '.bold + 'to exit'.red);
|
||||
process.stdout.write('\n\n'.reset);
|
||||
cproc.spawn('tail', ['-F', './logs/output.log'], {
|
||||
cwd: __dirname,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
break;
|
||||
|
||||
case 'setup':
|
||||
cproc.fork('app.js', ['--setup'], {
|
||||
cwd: __dirname,
|
||||
silent: false
|
||||
});
|
||||
break;
|
||||
|
||||
case 'reset':
|
||||
var args = process.argv.slice(0);
|
||||
args.unshift('--reset');
|
||||
fork(args);
|
||||
break;
|
||||
|
||||
case 'activate':
|
||||
var args = process.argv.slice(0);
|
||||
args.unshift('--activate');
|
||||
fork(args);
|
||||
break;
|
||||
|
||||
case 'plugins':
|
||||
var args = process.argv.slice(0);
|
||||
args.unshift('--plugins');
|
||||
fork(args);
|
||||
break;
|
||||
|
||||
case 'upgrade-plugins':
|
||||
upgradePlugins();
|
||||
break;
|
||||
|
||||
case 'upgrade':
|
||||
async.series([
|
||||
function(next) {
|
||||
process.stdout.write('1. '.bold + 'Bringing base dependencies up to date... '.yellow);
|
||||
require('child_process').execFile('/usr/bin/env', ['npm', 'i', '--production'], { stdio: 'ignore' }, next);
|
||||
},
|
||||
function(next) {
|
||||
process.stdout.write('OK\n'.green);
|
||||
process.stdout.write('2. '.bold + 'Checking installed plugins for updates... '.yellow);
|
||||
upgradePlugins(next);
|
||||
},
|
||||
function(next) {
|
||||
process.stdout.write('3. '.bold + 'Updating NodeBB data store schema...\n'.yellow);
|
||||
var upgradeProc = cproc.fork('app.js', ['--upgrade'], {
|
||||
cwd: __dirname,
|
||||
silent: false
|
||||
});
|
||||
|
||||
upgradeProc.on('close', next)
|
||||
}
|
||||
], function(err) {
|
||||
if (err) {
|
||||
process.stdout.write('\nError'.red + ': ' + err.message + '\n');
|
||||
} else {
|
||||
var message = 'NodeBB Upgrade Complete!';
|
||||
// some consoles will return undefined/zero columns, so just use 2 spaces in upgrade script if we can't get our column count
|
||||
var columns = process.stdout.columns;
|
||||
var spaces = columns ? new Array(Math.floor(columns / 2) - (message.length / 2) + 1).join(' ') : " ";
|
||||
|
||||
process.stdout.write('OK\n'.green);
|
||||
process.stdout.write('\n' + spaces + message.green.bold + '\n\n'.reset);
|
||||
}
|
||||
});
|
||||
break;
|
||||
|
||||
default:
|
||||
process.stdout.write('\nWelcome to NodeBB\n\n'.bold);
|
||||
process.stdout.write('Usage: ./nodebb {start|stop|reload|restart|log|setup|reset|upgrade|dev}\n\n');
|
||||
process.stdout.write('\t' + 'start'.yellow + '\tStart the NodeBB server\n');
|
||||
process.stdout.write('\t' + 'stop'.yellow + '\tStops the NodeBB server\n');
|
||||
process.stdout.write('\t' + 'reload'.yellow + '\tRestarts NodeBB\n');
|
||||
process.stdout.write('\t' + 'restart'.yellow + '\tRestarts NodeBB\n');
|
||||
process.stdout.write('\t' + 'log'.yellow + '\tOpens the logging interface (useful for debugging)\n');
|
||||
process.stdout.write('\t' + 'setup'.yellow + '\tRuns the NodeBB setup script\n');
|
||||
process.stdout.write('\t' + 'reset'.yellow + '\tDisables all plugins, restores the default theme.\n');
|
||||
process.stdout.write('\t' + 'activate'.yellow + '\tActivate a plugin on start up.\n');
|
||||
process.stdout.write('\t' + 'plugins'.yellow + '\tList all plugins that have been installed.\n');
|
||||
process.stdout.write('\t' + 'upgrade'.yellow + '\tRun NodeBB upgrade scripts, ensure packages are up-to-date\n');
|
||||
process.stdout.write('\t' + 'dev'.yellow + '\tStart NodeBB in interactive development mode\n');
|
||||
process.stdout.write('\n'.reset);
|
||||
break;
|
||||
}
|
||||
|
||||
123
nodebb.bat
@@ -1 +1,122 @@
|
||||
@echo off && cd %~dp0 && node ./src/cli %*
|
||||
@echo off
|
||||
|
||||
rem %1 action
|
||||
rem %2 subaction
|
||||
|
||||
setlocal enabledelayedexpansion
|
||||
2>nul call :CASE_%1
|
||||
if ERRORLEVEL 1 call :DEFAULT_CASE
|
||||
|
||||
exit /B
|
||||
|
||||
:CASE_start
|
||||
echo Starting NodeBB
|
||||
echo "nodebb.bat stop" to stop the NodeBB server
|
||||
echo "nodebb.bat log" to view server output
|
||||
|
||||
rem Start the loader daemon
|
||||
node loader %*
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:CASE_stop
|
||||
call :pidexists
|
||||
if %_result%==0 (
|
||||
echo NodeBB is already stopped.
|
||||
) else (
|
||||
echo Stopping NodeBB. Goodbye!
|
||||
|
||||
rem Doing this forcefully is probably not the best method
|
||||
taskkill /PID !_pid! /f>nul
|
||||
)
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:CASE_restart
|
||||
echo Unsupported
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:CASE_reload
|
||||
echo Unsupported
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:CASE_status
|
||||
call :pidexists
|
||||
if %_result%==0 (
|
||||
echo NodeBB is not running
|
||||
echo "nodebb.bat start" to launch the NodeBB server
|
||||
) else (
|
||||
echo NodeBB Running ^(pid !_pid!^)
|
||||
echo "nodebb.bat stop" to stop the NodeBB server
|
||||
echo "nodebb.bat log" to view server output
|
||||
echo "nodebb.bat restart" to restart NodeBB
|
||||
)
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:CASE_log
|
||||
cls
|
||||
type .\logs\output.log
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:CASE_upgrade
|
||||
call npm install
|
||||
call npm i nodebb-theme-vanilla nodebb-theme-lavender nodebb-widget-essentials
|
||||
node app --upgrade
|
||||
copy /b package.json +,,>nul
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:CASE_setup
|
||||
node app --setup %*
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:CASE_reset
|
||||
node app --reset --%2
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:CASE_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
|
||||
set NODE_ENV=development
|
||||
node loader --no-daemon %*
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:CASE_watch
|
||||
echo Not supported
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:DEFAULT_CASE
|
||||
echo Welcome to NodeBB
|
||||
echo Usage: nodebb.bat ^{start^|stop^|reload^|restart^|log^|setup^|reset^|upgrade^|dev^|watch^}
|
||||
|
||||
goto END_CASE
|
||||
|
||||
:END_CASE
|
||||
endlocal
|
||||
VER > NUL
|
||||
goto :EOF
|
||||
|
||||
:pidexists
|
||||
if exist %~dp0pidfile (
|
||||
set /p _pid=<pidfile
|
||||
|
||||
for /f "usebackq" %%Z in (`tasklist /nh /fi "PID eq !_pid!"`) do (
|
||||
if %%Z==INFO: (
|
||||
del pidfile
|
||||
set _result=0
|
||||
) else (
|
||||
set _result=1
|
||||
)
|
||||
)
|
||||
) else (
|
||||
set _result=0
|
||||
)
|
||||
120
package.json
Normal file
@@ -0,0 +1,120 @@
|
||||
{
|
||||
"name": "nodebb",
|
||||
"license": "GPL-3.0",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "1.1.3-auto.0",
|
||||
"homepage": "http://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/NodeBB/NodeBB/"
|
||||
},
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"start": "node loader.js",
|
||||
"test": "./node_modules/.bin/istanbul cover ./node_modules/.bin/_mocha -- ./tests -t 10000"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": "~1.5.0",
|
||||
"autoprefixer": "^6.2.3",
|
||||
"bcryptjs": "~2.3.0",
|
||||
"body-parser": "^1.9.0",
|
||||
"chart.js": "^2.1.0",
|
||||
"colors": "^1.1.0",
|
||||
"compression": "^1.1.0",
|
||||
"connect-ensure-login": "^0.1.1",
|
||||
"connect-flash": "^0.1.1",
|
||||
"connect-mongo": "~1.1.0",
|
||||
"connect-multiparty": "^2.0.0",
|
||||
"connect-redis": "~3.1.0",
|
||||
"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",
|
||||
"express-useragent": "0.2.4",
|
||||
"html-to-text": "2.0.0",
|
||||
"ip": "1.1.2",
|
||||
"jimp": "0.2.21",
|
||||
"json-2-csv": "^2.0.22",
|
||||
"less": "^2.0.0",
|
||||
"logrotate-stream": "^0.2.3",
|
||||
"lru-cache": "4.0.0",
|
||||
"mime": "^1.3.4",
|
||||
"minimist": "^1.1.1",
|
||||
"mkdirp": "~0.5.0",
|
||||
"mongodb": "~2.1.3",
|
||||
"morgan": "^1.3.2",
|
||||
"mousetrap": "^1.5.3",
|
||||
"nconf": "~0.8.2",
|
||||
"nodebb-plugin-composer-default": "4.1.8",
|
||||
"nodebb-plugin-dbsearch": "1.0.2",
|
||||
"nodebb-plugin-emoji-extended": "1.1.1",
|
||||
"nodebb-plugin-emoji-one": "1.1.5",
|
||||
"nodebb-plugin-markdown": "6.0.2",
|
||||
"nodebb-plugin-mentions": "1.1.3",
|
||||
"nodebb-plugin-soundpack-default": "0.1.6",
|
||||
"nodebb-plugin-spam-be-gone": "0.4.10",
|
||||
"nodebb-rewards-essentials": "0.0.9",
|
||||
"nodebb-theme-lavender": "3.0.13",
|
||||
"nodebb-theme-persona": "4.1.20",
|
||||
"nodebb-theme-vanilla": "5.1.9",
|
||||
"nodebb-widget-essentials": "2.0.10",
|
||||
"nodemailer": "2.0.0",
|
||||
"nodemailer-sendmail-transport": "1.0.0",
|
||||
"nodemailer-smtp-transport": "^2.4.1",
|
||||
"passport": "^0.3.0",
|
||||
"passport-local": "1.0.0",
|
||||
"postcss": "^5.0.13",
|
||||
"prompt": "^1.0.0",
|
||||
"redis": "~2.6.2",
|
||||
"request": "^2.44.0",
|
||||
"rimraf": "~2.5.0",
|
||||
"rss": "^1.0.0",
|
||||
"semver": "^5.1.0",
|
||||
"serve-favicon": "^2.1.5",
|
||||
"sitemap": "^1.4.0",
|
||||
"socket.io": "^1.4.0",
|
||||
"socket.io-client": "^1.4.0",
|
||||
"socket.io-redis": "^1.0.0",
|
||||
"socketio-wildcard": "~0.3.0",
|
||||
"string": "^3.0.0",
|
||||
"templates.js": "0.3.4",
|
||||
"toobusy-js": "^0.5.1",
|
||||
"uglify-js": "^2.6.0",
|
||||
"underscore": "^1.8.3",
|
||||
"underscore.deep": "^0.5.1",
|
||||
"validator": "^5.0.0",
|
||||
"winston": "^2.1.0",
|
||||
"xregexp": "~3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"grunt": "~0.4.5",
|
||||
"grunt-contrib-watch": "^1.0.0",
|
||||
"istanbul": "^0.4.2",
|
||||
"mocha": "~1.13.0"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/NodeBB/NodeBB/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
},
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Excessive Load Warning</title>
|
||||
<link href='https://fonts.googleapis.com/css?family=Ubuntu:400,500,700' rel='stylesheet' type='text/css'>
|
||||
<link href='http://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 {
|
||||
|
||||
|
Before Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.7 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 4.1 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 47 KiB |
|
Before Width: | Height: | Size: 3.6 KiB |
@@ -2,6 +2,6 @@
|
||||
The following stylesheet is only included on pages that can execute javascript
|
||||
*/
|
||||
|
||||
.page-topic [component="post/content"] img:not(.not-responsive):not([data-state]) {
|
||||
[component="post/content"] img:not(.not-responsive):not([data-state]) {
|
||||
display: none !important;
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"alert.confirm-reload": "هل تريد بالتأكيد إعادة تحميل NodeBB؟",
|
||||
"alert.confirm-restart": "هل تريد بالتأكيد إعادة تشغيل NodeBB؟",
|
||||
|
||||
"acp-title": "لوحة تحكم إدارة NodeBB | %1",
|
||||
"settings-header-contents": "محتويات"
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"post-cache": "التخزين المؤقت للمشاركات",
|
||||
"posts-in-cache": "المشاركات المخزنة مؤقتاً",
|
||||
"average-post-size": "متوسط حجم المشاركة",
|
||||
"length-to-max": "الطول / أقصى حد",
|
||||
"percent-full": "%1% Full",
|
||||
"post-cache-size": "حجم التخزين المؤقت للمشاركات",
|
||||
"items-in-cache": "العناصر في التخزين المؤقت",
|
||||
"control-panel": "لوحة التحكم",
|
||||
"update-settings": "تحديث إعدادات التخزين المؤقت"
|
||||
}
|
||||
@@ -1,36 +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.raw-info": "MongoDB Raw Info",
|
||||
|
||||
"redis": "Redis",
|
||||
"redis.version": "Redis Version",
|
||||
"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.keyspace-hits": "Keyspace Hits",
|
||||
"redis.keyspace-misses": "Keyspace Misses",
|
||||
"redis.raw-info": "Redis Raw Info"
|
||||
}
|
||||
@@ -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 لم يتم العثور\" تم محوها بنجاح"
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"events": "أحداث",
|
||||
"no-events": "لا توجد أحداث",
|
||||
"control-panel": "لوحة تحكم الأحداث",
|
||||
"delete-events": "حذف الأحداث"
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"logs": "السجلات",
|
||||
"control-panel": "لوحة تحكم السجلات",
|
||||
"reload": "إعادة تحميل السجلات",
|
||||
"clear": "محو السجلات",
|
||||
"clear-success": "تم محو السجلات!"
|
||||
}
|
||||
@@ -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": "أدخل HTML الخاصة بك هنا (مثل العلامات الوصفية، وما إلى ذلك)، والتي سيتم إلحاقها الى<code><head></code> في تصميم المنتدى. يسمح باستخدام علامات النص البرمجي، ولكن سيتم تعطيلها؛ لتوفر تبويبة\n<a href=\"#custom-header\" data-toggle=\"tab\">Javascript المخصصة</a>",
|
||||
"custom-header.enable": "تفعيل الترويسة المخصصة",
|
||||
|
||||
"custom-css.livereload": "تفعيل إعادة التحميل المباشرة",
|
||||
"custom-css.livereload.description": "فعل هذا الخيار لإجبار جميع الجلسات في الأجهزة التي قمت بتسجيل الدخول فيها بحسابك على التحديث عند النقر على زر الحفظ"
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"loading": "جاري تحميل السمات...",
|
||||
"homepage": "الصفحة الرئيسية",
|
||||
"select-skin": "إختيار السمة",
|
||||
"current-skin": "السمة الحالية",
|
||||
"skin-updated": "تم تحديث السمة",
|
||||
"applied-success": "تم تطبيق السمة %1 بنجاح",
|
||||
"revert-success": "تم إستعادة الألوان الاساسية للسمة"
|
||||
}
|
||||
@@ -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": "الرجاء إعادة تشغيل NodeBB لتنشيط هذا القالب بشكل كامل"
|
||||
}
|
||||
@@ -1,18 +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": "load",
|
||||
"uptime": "uptime",
|
||||
|
||||
"registered": "Registered",
|
||||
"sockets": "Sockets",
|
||||
"guests": "Guests",
|
||||
|
||||
"info": "Info"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"installed": "منصبة",
|
||||
"active": "مفعلة",
|
||||
"inactive": "معطلة",
|
||||
"out-of-date": "غير محدثة",
|
||||
"none-found": "لم يتم العثور على إضافات",
|
||||
"none-active": "لا توجد إضافات مفعلة",
|
||||
"find-plugins": "العثور على الإضافات",
|
||||
|
||||
"plugin-search": "البحث عن الإضافات",
|
||||
"plugin-search-placeholder": "جاري البحث عن الإضافات...",
|
||||
"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": "تعذر تحديد حالة هذه الإضافة، ربما بسبب خطأ في الإعدادات.",
|
||||
|
||||
"alert.enabled": "الإضافة مفعلة",
|
||||
"alert.disabled": "الإضافة معطلة",
|
||||
"alert.upgraded": "الإضافة مرقاة",
|
||||
"alert.installed": "الإضافة منصبة",
|
||||
"alert.uninstalled": "تم إلغاء تنصيب الإضافة",
|
||||
"alert.activate-success": "يرجى إعادة تشغيل NodeBB لتنشيط الإضافة بشكل بالكامل",
|
||||
"alert.deactivate-success": "تم تعطيل الإضافة بنجاح",
|
||||
"alert.upgrade-success": "يرجى إعادة تحميل NodeBB لترقية هذه الإضافة بشكل كامل",
|
||||
"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>",
|
||||
|
||||
"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": "هل ترغب بالاستمرار في تفعيل هذه الإضافة؟"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,22 +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"
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
{
|
||||
"forum-traffic": "Forum Traffic",
|
||||
"page-views": "مشاهدات الصفحات",
|
||||
"unique-visitors": "زائرين فريدين",
|
||||
"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.day": "يوم",
|
||||
"stats.week": "إسبوع",
|
||||
"stats.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>نسخة جديدة (الإصدار %1) تم إصدارها. خذ بعين الاعتبار <a href=\"https://docs.nodebb.org/configuring/upgrade/\">ترقية NodeBB الخاص بك</a>.</p>",
|
||||
"prerelease-upgrade-available": "<p>نسخة ما قبل الإصدار من NodeBB هذه قديمة. إصدار أحدث (الإصدار %1) تم إصداره. خذ بعين الاعتبار <a href=\"https://docs.nodebb.org/configuring/upgrade/\">ترقية NodeBB الخاص بك</a>.</p>",
|
||||
"prerelease-warning": "<p>هذه نسخة <strong>ماقبل الإصدار</strong> من NodeBB. قد تحدث أخطاء غير مقصودة. <i class=\"fa fa-exclamation-triangle\"></i></p>",
|
||||
"running-in-development": "المنتدى قيد التشغيل في وضع \"المطورين\". وقد تكون هناك ثغرات أمنية مفتوحة؛ من فضلك تواصل مع مسؤول نظامك.",
|
||||
|
||||
"notices": "إشعارات",
|
||||
"restart-not-required": "إعادة التشغيل غير مطلوب",
|
||||
"restart-required": "إعادة التشغيل مطلوبة",
|
||||
"search-plugin-installed": "إضافة البحث منصبة",
|
||||
"search-plugin-not-installed": "إضافة البحث غير منصبة",
|
||||
"search-plugin-tooltip": "نصب إضافة البحث من صفحة الإضافات البرمجية لتنشيط وظيفة البحث",
|
||||
|
||||
"control-panel": "التحكم بالنظام",
|
||||
"reload": "Restart",
|
||||
"restart": "Rebuild & 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.unique-visitors": "زوار فريدين",
|
||||
"graphs.registered-users": "مستخدمين مسجلين",
|
||||
"graphs.anonymous-users": "مستخدمين مجهولين"
|
||||
}
|
||||
@@ -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\")"
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"language-settings": "اعدادات اللغة",
|
||||
"description": "تُحدد اللغة الافتراضية إعدادات اللغة لجميع المستخدمين الذين يزورون المنتدى. <br />يمكن للأعضاء تجاوز اللغة الافتراضية من خلال صفحة إعدادات الحساب الخاصة بهم.",
|
||||
"default-language": "اللغة الافتراضية",
|
||||
"auto-detect": "الكشف عن إعدادات اللغة للزوار بشكل آلي"
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"icon": "Icon:",
|
||||
"change-icon": "change",
|
||||
"route": "Route:",
|
||||
"tooltip": "Tooltip:",
|
||||
"text": "Text:",
|
||||
"text-class": "Text Class: <small>optional</small>",
|
||||
"id": "ID: <small>optional</small>",
|
||||
|
||||
"properties": "Properties:",
|
||||
"only-admins": "Only display to Admins",
|
||||
"only-global-mods-and-admins": "Only display to Global Moderators and Admins",
|
||||
"only-logged-in": "Only display to logged in users",
|
||||
"only-guest": "Only display to guests",
|
||||
"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"
|
||||
}
|
||||
@@ -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!"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,71 +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-settings": "Copy Settings From",
|
||||
"optional-clone-settings": "(Optional) Clone Settings From Category",
|
||||
"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 this category in this section. Privileges can be granted on a per-user or a per-group basis. You can add a new user to this table by searching for them in the form below.",
|
||||
"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-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.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.",
|
||||
|
||||
"analytics.back": "Back to Categories List",
|
||||
"analytics.title": "Analytics for \"%1\" category",
|
||||
"analytics.pageviews-hourly": "<strong>Figure 1</strong> – Hourly page views for this category</small>",
|
||||
"analytics.pageviews-daily": "<strong>Figure 2</strong> – Daily page views for this category</small>",
|
||||
"analytics.topics-daily": "<strong>Figure 3</strong> – Daily topics created in this category</small>",
|
||||
"analytics.posts-daily": "<strong>Figure 4</strong> – Daily posts made in this category</small>",
|
||||
|
||||
"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"
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
{
|
||||
"name": "اسم المجموعة",
|
||||
"description": "Group Description",
|
||||
"member-count": "Member Count",
|
||||
"system": "System Group",
|
||||
"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.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-requests": "Disable join requests",
|
||||
"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!"
|
||||
}
|
||||
@@ -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> – Blacklist hits per hour",
|
||||
"analytics.blacklist-daily": "<strong>Figure 2</strong> – Blacklist hits per day",
|
||||
"ip-banned": "IP banned"
|
||||
}
|
||||
@@ -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 → Post → Posting Restrictions</a> and enable <strong>Post Queue</strong>.",
|
||||
"user": "User",
|
||||
"category": "Category",
|
||||
"title": "Title",
|
||||
"content": "Content",
|
||||
"posted": "Posted",
|
||||
"reply-to": "Reply to \"%1\"",
|
||||
"content-editable": "You can click on individual content to edit before posting."
|
||||
}
|
||||
@@ -1,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 → User → 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?"
|
||||
}
|
||||
@@ -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!"
|
||||
}
|
||||
@@ -1,99 +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",
|
||||
"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",
|
||||
"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",
|
||||
|
||||
"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.validate-email-success": "Emails validated",
|
||||
"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": "Email: ",
|
||||
"alerts.email-sent-to": "An invitation email has been sent to %1",
|
||||
"alerts.x-users-found": "%1 user(s) found! Search took %2 ms."
|
||||
}
|
||||
@@ -1,78 +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",
|
||||
|
||||
"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/logs": "Logs",
|
||||
"advanced/errors": "Errors",
|
||||
"advanced/cache": "Cache",
|
||||
"development/logger": "Logger",
|
||||
"development/info": "Info",
|
||||
|
||||
"reload-forum": "Reload 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..."
|
||||
}
|
||||
@@ -1,19 +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.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-help": "To deny access to all sites, leave empty",
|
||||
"headers.acam": "Access-Control-Allow-Methods",
|
||||
"headers.acah": "Access-Control-Allow-Headers",
|
||||
"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)"
|
||||
}
|
||||
@@ -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 before users are allowed to edit chat messages after posting. (0 disabled)",
|
||||
"restrictions.seconds-delete-after": "Number of seconds before users are allowed to delete chat messages after posting. (0 disabled)"
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"eu-consent": "EU Consent",
|
||||
"consent.enabled": "تفعيل",
|
||||
"consent.message": "Notification message",
|
||||
"consent.acceptance": "Acceptance message",
|
||||
"consent.link-text": "Policy Link Text",
|
||||
"consent.blank-localised-default": "Leave blank to use NodeBB localised defaults",
|
||||
"settings": "Settings",
|
||||
"cookie-domain": "Session cookie domain",
|
||||
"blank-default": "Leave blank for default"
|
||||
}
|
||||
@@ -1,37 +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.",
|
||||
|
||||
"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 Subscriptions",
|
||||
"subscriptions.disable": "Disable subscriber notification emails",
|
||||
"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>"
|
||||
}
|
||||
@@ -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. {pageTitle} | {browserTitle}",
|
||||
"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"
|
||||
}
|
||||
@@ -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-creation": "Allow Group Creation",
|
||||
"allow-creation-help": "If enabled, users can create groups <em>(Default: disabled)</em>",
|
||||
"max-name-length": "Maximum Group Name 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"
|
||||
}
|
||||
@@ -1,8 +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\"",
|
||||
"privileges": "Guest Privileges",
|
||||
"privileges.can-search": "Allow guests to search without logging in",
|
||||
"privileges.can-search-users": "Allow guests to search users without logging in"
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"notifications": "Notifications",
|
||||
"welcome-notification": "Welcome Notification",
|
||||
"welcome-notification-link": "Welcome Notification Link",
|
||||
"welcome-notification-uid": "Welcome Notification User (UID)"
|
||||
}
|
||||
@@ -1,11 +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",
|
||||
"initial-num-load": "Initial Number of Topics to Load on Unread, Recent, and Popular"
|
||||
}
|
||||
@@ -1,53 +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",
|
||||
"restrictions": "Posting Restrictions",
|
||||
"restrictions-new": "New User Restrictions",
|
||||
"restrictions.post-queue": "Enable 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": "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-defore-new": "Seconds before new user can post",
|
||||
"restrictions.seconds-edit-after": "Number of seconds before users are allowed to edit posts after posting. (0 disabled)",
|
||||
"restrictions.seconds-delete-after": "Number of seconds before users are allowed to delete posts after posting. (0 disabled)",
|
||||
"restrictions.replies-no-delete": "Number of replies after users are disallowed to delete their own topics. (0 disabled)",
|
||||
"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 & 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>",
|
||||
"teaser": "Teaser Post",
|
||||
"teaser.last-post": "Last – Show the latest post, including the original post, if no replies",
|
||||
"teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies",
|
||||
"teaser.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"
|
||||
}
|
||||
@@ -1,12 +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"
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"reconnection": "Reconnection Settings",
|
||||
"max-attempts": "Max Reconnection Attempts",
|
||||
"default-placeholder": "Default: %1",
|
||||
"delay": "Reconnection Delay"
|
||||
}
|
||||
@@ -1,12 +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.",
|
||||
"privacy": "Privacy",
|
||||
"list-private": "Make the tags list private",
|
||||
"related-topics": "Related Topics",
|
||||
"max-related-topics": "Maximum related topics to display (if supported by theme)"
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
{
|
||||
"posts": "المشاركات",
|
||||
"allow-files": "السماح للأعضاء بتحميل الملفات الإعتيادية",
|
||||
"private": "جعل الملفات التي تم رفعها خاصة",
|
||||
"max-image-width": "تغيير حجم الصور إلى عرض محدد (بالبكسل)",
|
||||
"max-image-width-help": "(بالبكسل، الافتراضي: 760 بكسل، ضع إلى 0 لتعطيل الخاصية)",
|
||||
"max-file-size": "الحد الأقصى لحجم الملف (بالكيبيبايت)",
|
||||
"max-file-size-help": "(بالكيبيبايت، الافتراضي: 2048)",
|
||||
"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": "اضف صور الغلاف الافتراضية متبوعة بفواصل لاستخدامها في الحسابات التي لا تحتوي على صور غلاف مرفوعة"
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
{
|
||||
"authentication": "المصادقة",
|
||||
"allow-local-login": "السماح بتسجيل الدخول المحلي",
|
||||
"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": "إعدادت الحساب",
|
||||
"disable-username-changes": "عدم السماح بتغيير اسم المستخدم",
|
||||
"disable-email-changes": "عدم السماح بتغيير البريد الالكتروني",
|
||||
"disable-password-changes": "عدم السماح بتغيير كلمة المرور",
|
||||
"allow-account-deletion": "السماح بحذف الحساب",
|
||||
"user-info-private": "إخفاء قائمة المستخدم والبيانات عن الزوار",
|
||||
"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": "فرض عملية تغيير كلمة المرور بعد مرور عدد محدد من الأيام",
|
||||
"registration": "تسجيل المستخدم",
|
||||
"registration-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": "عادي - بإمكان المستخدمين التسجيل من خلال صفحة /التسجيل.<br/>\nبموافقة الإدارة - يتم وضع قائمة تسجيلات المستخدمين في <a href=\"%1/admin/manage/registration\">قائمة إنتظار موافقة</a> الإدارة.<br/>\nبموافقة الإدارة لعناوين IP - عادي بالنسبة للمستخدمين الجدد، ويتطلب موافقة الإدارة للتسجيل من عناوين IP تم التسجيل بإستخدامها مسبقاً.<br/>\nبالدعوات فقط - بإمكان المستخدمين إرسال دعوات من خلال صفحة <a href=\"%1/users\" target=\"_blank\">الأعضاء</a>.<br/>\nبالدعوات من قبل الإدارة فقط - فقط المراء بإمكانهم إرسال الدعوات من خلال صفحة <a href=\"%1/users\" target=\"_blank\">الأعضاء</a> و صفحات <a href=\"%1/admin/manage/users\">الإدارة/المشرفين/الأعضاء</a>.<br/>\nلا يوجد تسجيل - إغلاق خيار التسجيل بالكامل.<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"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,20 +1,20 @@
|
||||
{
|
||||
"category": "قسم",
|
||||
"subcategories": "قسم فرعي",
|
||||
"category": "فئة",
|
||||
"subcategories": "فئة فرعية",
|
||||
"new_topic_button": "موضوع جديد",
|
||||
"guest-login-post": "سجل الدخول للمشاركة",
|
||||
"no_topics": "<strong>لا توجد مواضيع في هذه القسم</strong>لم لا تحاول إنشاء موضوع؟<br />",
|
||||
"guest-login-post": "سجل بالدخول للرد",
|
||||
"no_topics": "<strong>لا توجد مواضيع في هذه الفئة</strong>لم لا تحاول إنشاء موضوع؟<br />",
|
||||
"browsing": "تصفح",
|
||||
"no_replies": "لم يرد أحد",
|
||||
"no_new_posts": "لا توجد مشاركات جديدة.",
|
||||
"share_this_category": "شارك هذا القسم",
|
||||
"no_new_posts": "لا يوجد مشاركات جديدة.",
|
||||
"share_this_category": "انشر هذه الفئة",
|
||||
"watch": "تابع",
|
||||
"ignore": "تجاهل",
|
||||
"watching": "متابع",
|
||||
"ignoring": "متجاهل",
|
||||
"watching.description": "أظهر المواضيع في غير مقروء",
|
||||
"ignoring.description": "لا تظهر المواضيع في غير مقروء",
|
||||
"watch.message": "أنت الآن تتابع التحديثات من هذا القسم وجميع الأقسام الفرعية",
|
||||
"ignore.message": "أنت الآن تتجاهل التحديثات من هذا القسم وجميع الأقسام الفرعية",
|
||||
"watched-categories": "الأقسام المُتابعة"
|
||||
"watch.message": "أنت اﻷن تتابع تحديثات هذه الفئة",
|
||||
"ignore.message": "أنت اﻷن تتجاهل تحديثات هذه الفئة",
|
||||
"watched-categories": "الفئات المتابعة"
|
||||
}
|
||||
@@ -30,12 +30,7 @@
|
||||
"notif.chat.unsub.info": "تم إرسال هذا الإشعار بوجودة محادثة جديدة وفقا لخيارات تسجيلك.",
|
||||
"notif.post.cta": "انقر هنا لقراءة الموضوع بأكمله",
|
||||
"notif.post.unsub.info": "تم إشعارك بهذه المشاركة بناءً على الخيارات التي سبق وأن حددتها.",
|
||||
"notif.cta": "Click here to go to forum",
|
||||
"test.text1": "هذه رسالة تجريبية للتأكد من صحة إعدادت الرسائل الإلكترونية في منتدى NodeBB خاصتك.",
|
||||
"unsub.cta": "انقر هنا لتغيير تلك الإعدادات",
|
||||
"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": "شكرًا لك!"
|
||||
}
|
||||
@@ -1,43 +1,37 @@
|
||||
{
|
||||
"invalid-data": "بيانات غير صحيحة",
|
||||
"invalid-json": "Invalid JSON",
|
||||
"not-logged-in": "لم تقم بتسجيل الدخول",
|
||||
"account-locked": "تم حظر حسابك مؤقتًا.",
|
||||
"search-requires-login": "البحث في المنتدى يتطلب حساب - الرجاء تسجيل الدخول أو التسجيل",
|
||||
"goback": "Press back to return to the previous page",
|
||||
"invalid-cid": "قائمة غير موجودة",
|
||||
"invalid-tid": "موضوع غير متواجد",
|
||||
"invalid-pid": "رد غير موجود",
|
||||
"invalid-uid": "مستخدم غير موجود",
|
||||
"invalid-username": "اسم المستخدم غير مقبول",
|
||||
"invalid-email": "البريد الاكتروني غير مقبول",
|
||||
"invalid-title": "Invalid title",
|
||||
"invalid-title": "عنوان غير صحيح",
|
||||
"invalid-user-data": "بيانات المستخدم غير صحيحة",
|
||||
"invalid-password": "كلمة السر غير مقبولة",
|
||||
"invalid-login-credentials": "Invalid login credentials",
|
||||
"invalid-username-or-password": "المرجود تحديد اسم مستخدم و كلمة مرور",
|
||||
"invalid-search-term": "كلمة البحث غير صحيحة",
|
||||
"invalid-url": "Invalid URL",
|
||||
"csrf-invalid": "لم تتمكن من تسجيل الدخول. هنالك أحتمال ان جلستك انتهت. رجاءًا حاول مرة اخرى.",
|
||||
"invalid-pagination-value": "رقم الصفحة غير صحيح ، يجب أن يكون بين %1 و %2 .",
|
||||
"csrf-invalid": "We were unable to log you in, likely due to an expired session. Please try again",
|
||||
"invalid-pagination-value": "Invalid pagination value, must be at least %1 and at most %2",
|
||||
"username-taken": "اسم المستخدم مأخوذ",
|
||||
"email-taken": "البريد الالكتروني مأخوذ",
|
||||
"email-not-confirmed": "عنوان بريدك الإلكتروني غير مفعل بعد. انقر هنا لتفعيله من فضلك.",
|
||||
"email-not-confirmed-chat": "لا يمكنك الدردشة حتى تقوم بتأكيد بريدك الإلكتروني، الرجاء إضغط هنا لتأكيد بريدك اﻹلكتروني.",
|
||||
"email-not-confirmed-email-sent": "بريدك الإلكتروني غير مفعل. رجاءًا تفحص صندوق البريد عن رسالة التفعيل.",
|
||||
"email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email.",
|
||||
"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.",
|
||||
"username-too-short": "اسم المستخدم قصير.",
|
||||
"username-too-long": "اسم المستخدم طويل",
|
||||
"password-too-long": "كلمة السر طويلة ",
|
||||
"password-too-long": "Password too long",
|
||||
"user-banned": "المستخدم محظور",
|
||||
"user-banned-reason": "Sorry, this account has been banned (Reason: %1)",
|
||||
"user-banned-reason-until": "Sorry, this account has been banned until %1 (Reason: %2)",
|
||||
"user-too-new": "عذرا, يجب أن تنتظر 1% ثواني قبل قيامك بأول مشاركة",
|
||||
"blacklisted-ip": "نأسف، لقد تم حظرك من استخدام وتصفح المنتدى. إذا كنت تعتقد أن هذا خطأ رجاءًا اتصل بالإدارة. ",
|
||||
"ban-expiry-missing": "رجاءًا ضع تاريخ نهاية الحظر. ",
|
||||
"blacklisted-ip": "Sorry, your IP address has been banned from this community. If you feel this is in error, please contact an administrator.",
|
||||
"ban-expiry-missing": "Please provide an end date for this ban",
|
||||
"no-category": "قائمة غير موجودة",
|
||||
"no-topic": "موضوع غير موجود",
|
||||
"no-post": "رد غير موجود",
|
||||
@@ -47,102 +41,85 @@
|
||||
"no-privileges": "لاتملك الصلاحيات اللازمة للقيام بهذه العملية",
|
||||
"category-disabled": "قائمة معطلة",
|
||||
"topic-locked": "الموضوع مقفول",
|
||||
"post-edit-duration-expired": "يسمح لك بتعديل مشاركتك حتى %1 ثانية من نشرها",
|
||||
"post-edit-duration-expired-minutes": "يسمح لك بتعديل مشاركتك حتى %1 دقيقة من نشرها",
|
||||
"post-edit-duration-expired-minutes-seconds": "يسمح لك بتعديل مشاركتك حتى %1 دقيقة و %2 ثوان من نشرها",
|
||||
"post-edit-duration-expired-hours": "يسمح لك بتعديل مشاركتك حتى %1 ساعة من نشرها",
|
||||
"post-edit-duration-expired-hours-minutes": "يسمح لك بتعديل مشاركتك حتى %1 ساعة و %2 دقيقة من نشرها",
|
||||
"post-edit-duration-expired-days": "يسمح لك بتعديل مشاركتك حتى %1 يوم من نشرها",
|
||||
"post-edit-duration-expired-days-hours": "يسمح لك بتعديل مشاركتك حتى %1 يوم و %2 ساعة من نشرها",
|
||||
"post-delete-duration-expired": "يسمح لك بحذف مشاركتك حتى %1 ثانية من نشرها",
|
||||
"post-delete-duration-expired-minutes": "يسمح لك بحذف مشاركتك حتى %1 دقيقة من نشرها",
|
||||
"post-delete-duration-expired-minutes-seconds": "يسمح لك بحذف مشاركتك حتى %1 دقيقة و %2 ثوان من نشرها",
|
||||
"post-delete-duration-expired-hours": "يسمح لك بحذف مشاركتك حتى %1 ساعة من نشرها",
|
||||
"post-delete-duration-expired-hours-minutes": "يسمح لك بحذف مشاركتك حتى %1 ساعة و %2 دقيقة من نشرها",
|
||||
"post-delete-duration-expired-days": "يسمح لك بحذف مشاركتك حتى %1 يوم من نشرها",
|
||||
"post-delete-duration-expired-days-hours": "يسمح لك بحذف مشاركتك حتى %1 يوم و %2 ساعة من نشرها",
|
||||
"cant-delete-topic-has-reply": "لا يمكنك حذف مشاركة تم الرد عليها",
|
||||
"cant-delete-topic-has-replies": "لا يمكنك حذف مشاركة حصدت %1 ردود",
|
||||
"content-too-short": "يرجى ادخال موضوع أطول. على المواضيع أن تحتوي على %1 حرف على الأقل.",
|
||||
"content-too-long": "يرجى ادخال موضوع أقصر. على المواضيع أن لا تتخطى %1 حرف.",
|
||||
"title-too-short": "يرجى إدخال عنوان أطول. على العناوين أن تحتوي على %1 حرف على الأقل.",
|
||||
"title-too-long": "يرجى ادخال عنوان أقصر. على العناوين أن لا تتخطى %1 حرف.",
|
||||
"category-not-selected": "Category not selected.",
|
||||
"too-many-posts": "يسمح لك بالنشر مرة كل %1 ثانية - يرجى الإنتظار قبل النشر مجدداً",
|
||||
"post-edit-duration-expired": "You are only allowed to edit posts for %1 second(s) after posting",
|
||||
"post-edit-duration-expired-minutes": "You are only allowed to edit posts for %1 minute(s) after posting",
|
||||
"post-edit-duration-expired-minutes-seconds": "You are only allowed to edit posts for %1 minute(s) %2 second(s) after posting",
|
||||
"post-edit-duration-expired-hours": "You are only allowed to edit posts for %1 hour(s) after posting",
|
||||
"post-edit-duration-expired-hours-minutes": "You are only allowed to edit posts for %1 hour(s) %2 minute(s) after posting",
|
||||
"post-edit-duration-expired-days": "You are only allowed to edit posts for %1 day(s) after posting",
|
||||
"post-edit-duration-expired-days-hours": "You are only allowed to edit posts for %1 day(s) %2 hour(s) after posting",
|
||||
"post-delete-duration-expired": "You are only allowed to delete posts for %1 second(s) after posting",
|
||||
"post-delete-duration-expired-minutes": "You are only allowed to delete posts for %1 minute(s) after posting",
|
||||
"post-delete-duration-expired-minutes-seconds": "You are only allowed to delete posts for %1 minute(s) %2 second(s) after posting",
|
||||
"post-delete-duration-expired-hours": "You are only allowed to delete posts for %1 hour(s) after posting",
|
||||
"post-delete-duration-expired-hours-minutes": "You are only allowed to delete posts for %1 hour(s) %2 minute(s) after posting",
|
||||
"post-delete-duration-expired-days": "You are only allowed to delete posts for %1 day(s) after posting",
|
||||
"post-delete-duration-expired-days-hours": "You are only allowed to delete posts for %1 day(s) %2 hour(s) after posting",
|
||||
"content-too-short": "Please enter a longer post. Posts should contain at least %1 character(s).",
|
||||
"content-too-long": "Please enter a shorter post. Posts can't be longer than %1 character(s).",
|
||||
"title-too-short": "Please enter a longer title. Titles should contain at least %1 character(s).",
|
||||
"title-too-long": "Please enter a shorter title. Titles can't be longer than %1 character(s).",
|
||||
"too-many-posts": "You can only post once every %1 second(s) - please wait before posting again",
|
||||
"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",
|
||||
"tag-too-short": "Please enter a longer tag. Tags should contain at least %1 character(s)",
|
||||
"tag-too-long": "Please enter a shorter tag. Tags can't be longer than %1 character(s)",
|
||||
"not-enough-tags": "Not enough tags. Topics must have at least %1 tag(s)",
|
||||
"too-many-tags": "Too many tags. Topics can't have more than %1 tag(s)",
|
||||
"still-uploading": "الرجاء الانتظار حتى يكتمل الرفع.",
|
||||
"file-too-big": "الحد الأقصى لرفع الملفات %1 كيلو بت. رجاءًا ارفع ملف أصغر",
|
||||
"guest-upload-disabled": "خاصية رفع الملفات غير مفعلة للزوار.",
|
||||
"already-bookmarked": "You have already bookmarked this post",
|
||||
"already-unbookmarked": "You have already unbookmarked this post",
|
||||
"still-uploading": "الرجاء انتظار الرفع",
|
||||
"file-too-big": "Maximum allowed file size is %1 kB - please upload a smaller file",
|
||||
"guest-upload-disabled": "Guest uploading has been disabled",
|
||||
"already-favourited": "You have already bookmarked this post",
|
||||
"already-unfavourited": "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",
|
||||
"cant-remove-last-admin": "You are the only administrator. Add another user as an administrator before removing yourself as admin",
|
||||
"cant-delete-admin": "Remove administrator privileges from this account before attempting to delete it.",
|
||||
"invalid-image-type": "Invalid image type. Allowed types are: %1",
|
||||
"invalid-image-extension": "Invalid image extension",
|
||||
"invalid-file-type": "Invalid file type. Allowed types are: %1",
|
||||
"group-name-too-short": "اسم المجموعة قصير",
|
||||
"group-name-too-long": "اسم المجموعة طويل.",
|
||||
"group-name-too-long": "Group name too long",
|
||||
"group-already-exists": "المجموعة موجودة مسبقا",
|
||||
"group-name-change-not-allowed": "لايسمح بتغيير أسماء المجموعات",
|
||||
"group-already-member": "أنت عضو في هذه المجموعة.",
|
||||
"group-not-member": "أنت لست عضو في هذه المجموعة.",
|
||||
"group-already-member": "Already part of this group",
|
||||
"group-not-member": "Not a member of this group",
|
||||
"group-needs-owner": "هذه المجموعة تتطلب مالك واحد على اﻷقل",
|
||||
"group-already-invited": "المستخدم سبق وأن تمت دعوته",
|
||||
"group-already-requested": "سبق وتم تسجيل طلب العضوية",
|
||||
"group-already-invited": "This user has already been invited",
|
||||
"group-already-requested": "Your membership request has already been submitted",
|
||||
"post-already-deleted": "سبق وتم حذف هذا الرد",
|
||||
"post-already-restored": "سبق وتم إلغاء حذف هذا الرد",
|
||||
"topic-already-deleted": "سبق وتم حذف هذا الموضوع",
|
||||
"topic-already-restored": "سبق وتم إلغاء حذف هذا الرد",
|
||||
"cant-purge-main-post": "لا يمكنك محو المشاركة الأساسية، يرجى حذف الموضوع بدلاً عن ذلك",
|
||||
"cant-purge-main-post": "You can't purge the main post, please delete the topic instead",
|
||||
"topic-thumbnails-are-disabled": "الصور المصغرة غير مفعلة.",
|
||||
"invalid-file": "ملف غير مقبول",
|
||||
"uploads-are-disabled": "رفع الملفات غير مفعل",
|
||||
"signature-too-long": "عذرا، توقيعك يجب ألا يتجاوز %1 حرفًا.",
|
||||
"about-me-too-long": "نأسف، ( عني ) لا يمكن أن يكون أكثر من %1 حرف. ",
|
||||
"about-me-too-long": "Sorry, your about me cannot be longer than %1 character(s).",
|
||||
"cant-chat-with-yourself": "لايمكنك فتح محادثة مع نفسك",
|
||||
"chat-restricted": "هذا المستخدم عطل المحادثات الواردة عليه. يجب أن يتبعك حتى تتمكن من فتح محادثة معه.",
|
||||
"chat-disabled": "نظام المحادثة معطل.",
|
||||
"chat-disabled": "Chat system 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": "لقد شاركت بالتصويت ، ألا تذكر؟",
|
||||
"invalid-chat-message": "Invalid chat message",
|
||||
"chat-message-too-long": "Chat message is too long",
|
||||
"cant-edit-chat-message": "You are not allowed to edit this message",
|
||||
"cant-remove-last-user": "You can't remove the last user",
|
||||
"cant-delete-chat-message": "You are not allowed to delete this message",
|
||||
"already-voting-for-this-post": "You have already voted 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",
|
||||
"already-flagged": "لقد بلغت عن هذه المشاركة من قبل.",
|
||||
"self-vote": "You cannot vote on your own post",
|
||||
"already-flagged": "You have already flagged this 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": "المستخدم غير موجود في الغرفة.",
|
||||
"no-users-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!"
|
||||
"invite-maximum-met": "You have invited the maximum amount of people (%1 out of %2).",
|
||||
"no-session-found": "No login session found!",
|
||||
"not-in-room": "User not in room",
|
||||
"no-users-in-room": "No users in this room",
|
||||
"cant-kick-self": "You can't kick yourself from the group",
|
||||
"no-users-selected": "No user(s) selected",
|
||||
"invalid-home-page-route": "Invalid home page route"
|
||||
}
|
||||
@@ -1,64 +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.",
|
||||
|
||||
"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-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."
|
||||
}
|
||||
@@ -4,28 +4,25 @@
|
||||
"buttons.close": "أغلق",
|
||||
"403.title": "غير مسموح بالدخول",
|
||||
"403.message": "يبدو أنك قد تعثرت على صفحة لا تمتلك الصلاحية للدخول إليها",
|
||||
"403.login": "ربما يجب عليك <a href='%1/login'>تسجل دخولك</a>.",
|
||||
"403.login": "Perhaps you should <a href='%1/login'>try logging in</a>?",
|
||||
"404.title": "لم يتم العثور",
|
||||
"404.message": "الصفحة غير موجودة. العودة لـ <a href='%1/'>الرئيسية</a>",
|
||||
"500.title": "خطأ داخلي",
|
||||
"404.message": "You seem to have stumbled upon a page that does not exist. Return to the <a href='%1/'>home page</a>.",
|
||||
"500.title": "خطأ داخلي.",
|
||||
"500.message": "عفوا! يبدو وكأنه شيء ذهب على نحو خاطئ!",
|
||||
"400.title": "طلب سيئ",
|
||||
"400.message": "الرابط غير صحيح. رجاءًا تأكد من الرابط أو ارجع لـ <a href='%1/'>الرئيسية</a>",
|
||||
"register": "تسجيل",
|
||||
"login": "دخول",
|
||||
"please_log_in": "الرجاء تسجيل الدخول",
|
||||
"please_log_in": "المرجو تسجيل الدخول",
|
||||
"logout": "تسجيل الخروج",
|
||||
"posting_restriction_info": "إضافة مشاركات جديد حكر على الأعضاء المسجلين، انقر هنا لتسجيل الدخول.",
|
||||
"welcome_back": "مرحبًا بعودتك",
|
||||
"you_have_successfully_logged_in": "تم سجيل الدخول بنجاح",
|
||||
"save_changes": "حفظ التغييرات",
|
||||
"save": "حفظ",
|
||||
"close": "أغلق",
|
||||
"pagination": "الصفحات",
|
||||
"pagination.out_of": "%1 من %2",
|
||||
"pagination.enter_index": "أدخل الرقم التسلسلي",
|
||||
"header.admin": "مدير النظام",
|
||||
"header.categories": "الأقسام",
|
||||
"header.admin": "مدبر نظام",
|
||||
"header.categories": "الفئات",
|
||||
"header.recent": "حديث",
|
||||
"header.unread": "غير مقروء",
|
||||
"header.tags": "وسم",
|
||||
@@ -36,10 +33,10 @@
|
||||
"header.notifications": "التنبيهات",
|
||||
"header.search": "بحث",
|
||||
"header.profile": "ملف",
|
||||
"header.navigation": "الاستكشاف",
|
||||
"notifications.loading": "تحميل التنبيهات",
|
||||
"header.navigation": "Navigation",
|
||||
"notifications.loading": "تحميل التبليغات",
|
||||
"chats.loading": "تحميل الدردشات",
|
||||
"motd.welcome": "مرحبا بكم في NodeBB، منصة المناقشة المستقبلية.",
|
||||
"motd.welcome": "مرحبا بكم NodeBB، منصة مناقشة المستقبل",
|
||||
"previouspage": "الصفحة السابقة",
|
||||
"nextpage": "الصفحة التالية",
|
||||
"alert.success": "نجاح",
|
||||
@@ -52,33 +49,31 @@
|
||||
"users": "الأعضاء",
|
||||
"topics": "المواضيع",
|
||||
"posts": "المشاركات",
|
||||
"best": "الأفضل",
|
||||
"votes": "Votes",
|
||||
"upvoters": "الموافقين",
|
||||
"upvoted": "مصوت بالموجب",
|
||||
"downvoters": "مصوتين بالسالب",
|
||||
"downvoted": "مصوت بالسالب",
|
||||
"best": "Best",
|
||||
"upvoters": "Upvoters",
|
||||
"upvoted": "Upvoted",
|
||||
"downvoters": "Downvoters",
|
||||
"downvoted": "Downvoted",
|
||||
"views": "المشاهدات",
|
||||
"reputation": "السمعة",
|
||||
"read_more": "اقرأ المزيد",
|
||||
"more": "المزيد",
|
||||
"posted_ago_by_guest": "كتب %1 بواسطة زائر",
|
||||
"posted_ago_by": "كتب %1 بواسطة %2",
|
||||
"posted_ago_by_guest": "كتب %1 من طرف زائر",
|
||||
"posted_ago_by": "كتب %1 من طرف %2",
|
||||
"posted_ago": "كتب %1",
|
||||
"posted_in": "كتب في %1",
|
||||
"posted_in_by": "كتب في 1% بواسطة %2",
|
||||
"posted_in": "posted in %1",
|
||||
"posted_in_by": "posted in %1 by %2",
|
||||
"posted_in_ago": "كتب في %1 %2",
|
||||
"posted_in_ago_by": "كتب في %1 %2 من طرف %3",
|
||||
"user_posted_ago": "%1 كتب %2",
|
||||
"guest_posted_ago": "كتب زائر %1",
|
||||
"last_edited_by": "اخر تحرير بواسطة 1%",
|
||||
"last_edited_by": "last edited by %1",
|
||||
"norecentposts": "لاوجود لمشاركات جديدة",
|
||||
"norecenttopics": "لاوجود لمواضيع جديدة",
|
||||
"recentposts": "آخر المشاركات",
|
||||
"recentips": "آخر عناوين ال IP التي سجلت الدخول",
|
||||
"moderator_tools": "أدوات المشرف",
|
||||
"away": "غير متواجد",
|
||||
"dnd": "عدم الإزعاج",
|
||||
"dnd": "Do not disturb",
|
||||
"invisible": "مخفي",
|
||||
"offline": "غير متصل",
|
||||
"email": "عنوان البريد الإلكتروني",
|
||||
@@ -86,26 +81,16 @@
|
||||
"guest": "زائر",
|
||||
"guests": "الزوار",
|
||||
"updated.title": "تم تحديث المنتدى",
|
||||
"updated.message": "لقد تم تحديث المنتدى إلى آخر نسخة للتو. إضغط هنا لإعادة تحميل الصفحة.",
|
||||
"updated.message": "لقد تم تحديث المنتدى إلى آخر نسخة للتو. المرجو إعادة تحميل الصفحة.",
|
||||
"privacy": "الخصوصية",
|
||||
"follow": "متابعة",
|
||||
"unfollow": "إلغاء المتابعة",
|
||||
"delete_all": "حذف الكل",
|
||||
"map": "خريطة",
|
||||
"sessions": "الجلسة",
|
||||
"ip_address": "عنوان الآي بي",
|
||||
"enter_page_number": "ادخل رقم الصفحة",
|
||||
"upload_file": "ارفع ملف",
|
||||
"upload": "ارفع",
|
||||
"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..."
|
||||
"map": "Map",
|
||||
"sessions": "Login Sessions",
|
||||
"ip_address": "IP Address",
|
||||
"enter_page_number": "Enter page number",
|
||||
"upload_file": "Upload file",
|
||||
"upload": "Upload",
|
||||
"allowed-file-types": "Allowed file types are %1"
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
"details.disableJoinRequests": "Disable join requests",
|
||||
"details.grant": "منح/سحب المِلكية",
|
||||
"details.kick": "طرد",
|
||||
"details.kick_confirm": "Are you sure you want to remove this member from the group?",
|
||||
"details.owner_options": "إدارة المجموعة",
|
||||
"details.group_name": "اسم المجموعة",
|
||||
"details.member_count": "عدد اﻷعضاء",
|
||||
@@ -51,8 +50,5 @@
|
||||
"membership.leave-group": "غادر المجموعة",
|
||||
"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?"
|
||||
"upload-group-cover": "Upload group cover"
|
||||
}
|
||||
@@ -5,8 +5,8 @@
|
||||
"remember_me": "تذكرني؟",
|
||||
"forgot_password": "نسيت كلمة المرور؟",
|
||||
"alternative_logins": "تسجيلات الدخول البديلة",
|
||||
"failed_login_attempt": "تسجيل الدخول غير ناجح",
|
||||
"failed_login_attempt": "Login Unsuccessful",
|
||||
"login_successful": "قمت بتسجيل الدخول بنجاح!",
|
||||
"dont_have_account": "لا تملك حساب؟",
|
||||
"logged-out-due-to-inactivity": "لقد تم تسجيل خروجك من لوحة تحكم بسبب عدم نشاطك"
|
||||
"logged-out-due-to-inactivity": "You have been logged out of the Admin Control Panel due to inactivity"
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"chat.chatting_with": "Chat with",
|
||||
"chat.chatting_with": "الدردشة مع <span id=\"chat-with-name\"></span>",
|
||||
"chat.placeholder": "أكتب رسالة دردشة هنا، اضغط ENTER للإرسال",
|
||||
"chat.send": "أرسل",
|
||||
"chat.no_active": "لا يوجد لديك دردشات نشطة.",
|
||||
@@ -12,26 +12,14 @@
|
||||
"chat.recent-chats": "آخر الدردشات",
|
||||
"chat.contacts": "الأصدقاء",
|
||||
"chat.message-history": "تاريخ الرسائل",
|
||||
"chat.options": "Chat options",
|
||||
"chat.pop-out": "افتح الدردشة في نافذة خاصة",
|
||||
"chat.minimize": "Minimize",
|
||||
"chat.maximize": "تكبير",
|
||||
"chat.seven_days": "7 أيام",
|
||||
"chat.thirty_days": "30 يومًا",
|
||||
"chat.three_months": "3 أشهر",
|
||||
"chat.delete_message_confirm": "هل أنت متأكد من أنك تريد حذف هذه الرسالة؟",
|
||||
"chat.roomname": "Chat Room %1",
|
||||
"chat.add-users-to-room": "Add users to room",
|
||||
"chat.retrieving-users": "Retrieving users...",
|
||||
"chat.manage-room": "Manage Chat Room",
|
||||
"chat.add-user-help": "Search for users here. When selected, the user will be added to the chat. The new user will not be able to see chat messages written before they were added to the conversation.",
|
||||
"chat.confirm-chat-with-dnd-user": "This user has set their status to DnD(Do not disturb). Do you still want to chat with them?",
|
||||
"chat.rename-room": "Rename room",
|
||||
"chat.rename-placeholder": "Enter your room name here",
|
||||
"chat.rename-help": "The room name set here will be viewable by all participants in the room.",
|
||||
"chat.leave": "Leave Chat",
|
||||
"chat.leave-prompt": "Are you sure you wish to leave this chat?",
|
||||
"chat.leave-help": "Leaving this chat will remove you from future correspondence in this chat. If you are re-added in the future, you will not see any chat history from prior to your re-joining.",
|
||||
"chat.in-room": "In this room",
|
||||
"composer.compose": "اكتب",
|
||||
"composer.show_preview": "عرض المعاينة",
|
||||
"composer.hide_preview": "إخفاء المعاينة",
|
||||
@@ -50,7 +38,6 @@
|
||||
"composer.upload-picture": "Upload Image",
|
||||
"composer.upload-file": "Upload File",
|
||||
"composer.zen_mode": "Zen Mode",
|
||||
"composer.select_category": "Select a category",
|
||||
"bootbox.ok": "OK",
|
||||
"bootbox.cancel": "إلغاء",
|
||||
"bootbox.confirm": "تأكيد",
|
||||
|
||||
@@ -9,17 +9,7 @@
|
||||
"continue_to": "استمر إلى %1",
|
||||
"return_to": "عودة إى %1",
|
||||
"new_notification": "تنبيه جديد",
|
||||
"new_notification_from": "You have a new Notification from %1",
|
||||
"you_have_unread_notifications": "لديك تنبيهات غير مقروءة.",
|
||||
"all": "All",
|
||||
"topics": "Topics",
|
||||
"replies": "Replies",
|
||||
"chat": "Chats",
|
||||
"follows": "Follows",
|
||||
"upvote": "Upvotes",
|
||||
"new-flags": "New Flags",
|
||||
"my-flags": "Flags assigned to me",
|
||||
"bans": "Bans",
|
||||
"new_message_from": "رسالة جديدة من <strong>%1</strong>",
|
||||
"upvoted_your_post_in": "<strong>%1</strong> أضاف صوتًا إيجابيا إلى مشاركتك في <strong>%2</strong>.",
|
||||
"upvoted_your_post_in_dual": "<strong>%1</strong> and <strong>%2</strong> have upvoted your post in <strong>%3</strong>.",
|
||||
@@ -29,9 +19,6 @@
|
||||
"user_flagged_post_in": "<strong>%1</strong> أشعَرَ بمشاركة مخلة في <strong>%2</strong>",
|
||||
"user_flagged_post_in_dual": "<strong>%1</strong> and <strong>%2</strong> flagged a post in <strong>%3</strong>",
|
||||
"user_flagged_post_in_multiple": "<strong>%1</strong> and %2 others flagged a post in <strong>%3</strong>",
|
||||
"user_flagged_user": "<strong>%1</strong> flagged a user profile (%2)",
|
||||
"user_flagged_user_dual": "<strong>%1</strong> and <strong>%2</strong> flagged a user profile (%3)",
|
||||
"user_flagged_user_multiple": "<strong>%1</strong> and %2 others flagged a user profile (%3)",
|
||||
"user_posted_to": "<strong>%1</strong> أضاف ردا إلى: <strong>%2</strong>",
|
||||
"user_posted_to_dual": "<strong>%1</strong> and <strong>%2</strong> have posted replies to: <strong>%3</strong>",
|
||||
"user_posted_to_multiple": "<strong>%1</strong> and %2 others have posted replies to: <strong>%3</strong>",
|
||||
@@ -41,24 +28,8 @@
|
||||
"user_started_following_you_multiple": "<strong>%1</strong> and %2 others started following you.",
|
||||
"new_register": "<strong>%1</strong> sent a registration request.",
|
||||
"new_register_multiple": "There are <strong>%1</strong> registration requests awaiting review.",
|
||||
"flag_assigned_to_you": "<strong>Flag %1</strong> has been assigned to you",
|
||||
"post_awaiting_review": "Post awaiting review",
|
||||
"email-confirmed": "تم التحقق من عنوان البريد الإلكتروني",
|
||||
"email-confirmed-message": "شكرًا على إثبات صحة عنوان بريدك الإلكتروني. صار حسابك مفعلًا بالكامل.",
|
||||
"email-confirm-error-message": "حدث خطأ أثناء التحقق من عنوان بريدك الإلكتروني. ربما رمز التفعيل خاطئ أو انتهت صلاحيته.",
|
||||
"email-confirm-sent": "تم إرسال بريد التفعيل.",
|
||||
"none": "None",
|
||||
"notification_only": "Notification Only",
|
||||
"email_only": "Email Only",
|
||||
"notification_and_email": "Notification & Email",
|
||||
"notificationType_upvote": "When someone upvotes your post",
|
||||
"notificationType_new-topic": "When someone you follow posts a topic",
|
||||
"notificationType_new-reply": "When a new reply is posted in a topic you are watching",
|
||||
"notificationType_follow": "When someone starts following you",
|
||||
"notificationType_new-chat": "When you receive a chat message",
|
||||
"notificationType_group-invite": "When you receive a group invite",
|
||||
"notificationType_new-register": "When someone gets added to registration queue",
|
||||
"notificationType_post-queue": "When a new post is queued",
|
||||
"notificationType_new-post-flag": "When a post is flagged",
|
||||
"notificationType_new-user-flag": "When a user is flagged"
|
||||
"email-confirm-sent": "تم إرسال بريد التفعيل."
|
||||
}
|
||||
@@ -1,51 +1,43 @@
|
||||
{
|
||||
"home": "الصفحة الرئيسية",
|
||||
"unread": "المواضيع الغير مقروءة",
|
||||
"popular-day": "المواضيع الشائعة اليوم",
|
||||
"popular-week": "المواضيع الشائعة هذا الأسبوع",
|
||||
"popular-month": "المواضيع الشائعة هذا الشهر",
|
||||
"popular-alltime": "المواضيع الشائعة منذ القدم",
|
||||
"popular-day": "Popular topics today",
|
||||
"popular-week": "Popular topics this week",
|
||||
"popular-month": "Popular topics this month",
|
||||
"popular-alltime": "All time popular topics",
|
||||
"recent": "المواضيع الحديثة",
|
||||
"top": "Top Voted Topics",
|
||||
"moderator-tools": "Moderator Tools",
|
||||
"flagged-content": "Flagged Content",
|
||||
"ip-blacklist": "IP Blacklist",
|
||||
"post-queue": "Post Queue",
|
||||
"flagged-posts": "Flagged Posts",
|
||||
"users/online": "اﻷعضاء المتصلون",
|
||||
"users/latest": "أحدث اﻷعضاء",
|
||||
"users/sort-posts": "الأعضاء الأكثر نشاطاً",
|
||||
"users/sort-reputation": "الأعضاء الأفضل سمعة",
|
||||
"users/banned": "الأعضاء المحظورون",
|
||||
"users/sort-posts": "Users with the most posts",
|
||||
"users/sort-reputation": "Users with the most reputation",
|
||||
"users/banned": "Banned Users",
|
||||
"users/most-flags": "Most flagged users",
|
||||
"users/search": "بحث عن مستخدم",
|
||||
"users/search": "User Search",
|
||||
"notifications": "التنبيهات",
|
||||
"tags": "الكلمات الدلالية",
|
||||
"tag": "Topics tagged under "%1"",
|
||||
"tag": "Topics tagged under \"%1\"",
|
||||
"register": "تسجيل حساب",
|
||||
"registration-complete": "Registration complete",
|
||||
"login": "سجل الدخول الى حسابك",
|
||||
"login": "Login to your account",
|
||||
"reset": "إعادة تعيين كلمة مرور حسابك",
|
||||
"categories": "الفئات",
|
||||
"groups": "المجموعات",
|
||||
"group": "%1 مجموعة",
|
||||
"chats": "محادثات",
|
||||
"group": "%1 group",
|
||||
"chats": "Chats",
|
||||
"chat": "Chatting with %1",
|
||||
"flags": "Flags",
|
||||
"flag-details": "Flag %1 Details",
|
||||
"account/edit": "Editing \"%1\"",
|
||||
"account/edit/password": "Editing password of \"%1\"",
|
||||
"account/edit/username": "Editing username of \"%1\"",
|
||||
"account/edit/email": "Editing email of \"%1\"",
|
||||
"account/info": "معلومات الحساب",
|
||||
"account/info": "Account Info",
|
||||
"account/following": "People %1 follows",
|
||||
"account/followers": "People who follow %1",
|
||||
"account/posts": "Posts made by %1",
|
||||
"account/topics": "Topics created by %1",
|
||||
"account/groups": "%1's Groups",
|
||||
"account/bookmarks": "%1's Bookmarked Posts",
|
||||
"account/settings": "خيارات المستخدم",
|
||||
"account/favourites": "%1's Bookmarked Posts",
|
||||
"account/settings": "User Settings",
|
||||
"account/watched": "Topics watched by %1",
|
||||
"account/ignored": "Topics ignored by %1",
|
||||
"account/upvoted": "Posts upvoted by %1",
|
||||
"account/downvoted": "Posts downvoted by %1",
|
||||
"account/best": "Best posts made by %1",
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"year": "سنة",
|
||||
"alltime": "دائمًا",
|
||||
"no_recent_topics": "لايوجد مواضيع جديدة",
|
||||
"no_popular_topics": "لا يوجد مواضيع شائعة",
|
||||
"no_popular_topics": "There are no popular topics.",
|
||||
"there-is-a-new-topic": "يوجد موضوع جديد",
|
||||
"there-is-a-new-topic-and-a-new-post": "يوجد موضوع جديد و رد جديد",
|
||||
"there-is-a-new-topic-and-new-posts": "يوجد موضوع جديد و %1 ردود جديدة ",
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
"register": "تسجيل",
|
||||
"cancel_registration": "إلغاء التسجيل",
|
||||
"help.email": "افتراضيا، سيتم إخفاء بريدك الإلكتروني من العامة.",
|
||||
"help.username_restrictions": "اسم مستخدم فريدة من نوعها بين 1% و 2% حرفا. بإمكان الآخرين مناداتك بـ @<span id='yourUsername'>اسم المستخدم</span>.",
|
||||
"help.minimum_password_length": "كلمة المرور يجب أن تتكون على الأقل من 1% أحرف/حروف",
|
||||
"help.username_restrictions": "اسم مستخدم فريدة من نوعها بين1% و2% حرفا. يمكن للآخرين ذكرك @ <'span id='your-username> اسم المستخدم </span>.",
|
||||
"help.minimum_password_length": "كلمة المرور يجب أن تكون على الأقل بها 1% أحرف",
|
||||
"email_address": "عنوان البريد الإلكتروني",
|
||||
"email_address_placeholder": "ادخل عنوان البريد الإلكتروني",
|
||||
"username": "اسم المستخدم",
|
||||
@@ -16,8 +16,8 @@
|
||||
"alternative_registration": "طريقة تسجيل بديلة",
|
||||
"terms_of_use": "شروط الاستخدام",
|
||||
"agree_to_terms_of_use": "أوافق على شروط الاستخدام",
|
||||
"terms_of_use_error": "يجب عليك الموافقة على شروط الاستخدام",
|
||||
"registration-added-to-queue": "تمت إضافتك في قائمة الإنتضار. ستتلقى رسالة إلكترونية عند الموافقة على تسجيلك من قبل الإدارة.",
|
||||
"interstitial.intro": "نحتاج إلى بعض المعلومات الإضافية قبل أن نتمكن من إنشاء حسابك.",
|
||||
"interstitial.errors-found": "تعذر علينا إتمام عملية التسجيل:"
|
||||
"terms_of_use_error": "You must agree to the Terms of Use",
|
||||
"registration-added-to-queue": "Your registration has been added to the approval queue. You will receive an email when it is accepted by an administrator.",
|
||||
"interstitial.intro": "We require some additional information before we can create your account.",
|
||||
"interstitial.errors-found": "We could not complete your registration:"
|
||||
}
|
||||
@@ -8,11 +8,9 @@
|
||||
"posted-by": "مشاركة من طرف",
|
||||
"in-categories": "في الفئات",
|
||||
"search-child-categories": "بحث في الفئات الفرعية",
|
||||
"has-tags": "Has tags",
|
||||
"reply-count": "عدد المشاركات",
|
||||
"at-least": "على اﻷقل",
|
||||
"at-most": "على اﻷكثر",
|
||||
"relevance": "Relevance",
|
||||
"post-time": "تاريخ المشاركة",
|
||||
"newer-than": "أحدث من",
|
||||
"older-than": "أقدم من",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"success": "نجاح",
|
||||
"topic-post": "لقد تمت الإضافة بنجاح.",
|
||||
"post-queued": "Your post is queued for approval.",
|
||||
"authentication-successful": "تم تسجيل الدخول بنجاح",
|
||||
"settings-saved": "تم حفظ التغييرات!"
|
||||
}
|
||||
@@ -13,10 +13,7 @@
|
||||
"notify_me": "تلق تنبيهات بالردود الجديدة في هذا الموضوع",
|
||||
"quote": "اقتبس",
|
||||
"reply": "رد",
|
||||
"replies_to_this_post": "%1 Replies",
|
||||
"one_reply_to_this_post": "1 Reply",
|
||||
"last_reply_time": "Last reply",
|
||||
"reply-as-topic": "رد بموضوع",
|
||||
"reply-as-topic": "Reply as topic",
|
||||
"guest-login-reply": "يجب عليك تسجيل الدخول للرد",
|
||||
"edit": "تعديل",
|
||||
"delete": "حذف",
|
||||
@@ -27,75 +24,71 @@
|
||||
"link": "رابط",
|
||||
"share": "نشر",
|
||||
"tools": "أدوات",
|
||||
"flag": "تبليغ",
|
||||
"locked": "مقفل",
|
||||
"pinned": "مثبت",
|
||||
"moved": "منقول",
|
||||
"copy-ip": "Copy IP",
|
||||
"ban-ip": "Ban IP",
|
||||
"view-history": "Edit History",
|
||||
"bookmark_instructions": "اضغط هنا للعودة لأخر مشاركة مقروءة في الموضوع",
|
||||
"pinned": "Pinned",
|
||||
"moved": "Moved",
|
||||
"bookmark_instructions": "Click here to return to the last read post in this thread.",
|
||||
"flag_title": "إشعار بمشاركة مخلة.",
|
||||
"merged_message": "This topic has been merged into <a href=\"/topic/%1\">%2</a>",
|
||||
"flag_success": "تم الإشعار بهذه المشاركة على أنها مخلة",
|
||||
"deleted_message": "هذه المشاركة محذوفة. فقط من لهم صلاحية الإشراف على ا لمشاركات يمكنهم معاينتها.",
|
||||
"following_topic.message": "ستستلم تنبيها عند كل مشاركة جديدة في هذا الموضوع.",
|
||||
"not_following_topic.message": "You will see this topic in the unread topics list, but you will not receive notifications when somebody posts to this topic.",
|
||||
"ignoring_topic.message": "You will no longer see this topic in the unread topics list. You will be notified when you are mentioned or your post is up voted.",
|
||||
"login_to_subscribe": "المرجو إنشاء حساب أو تسجيل الدخول حتى يمكنك متابعة هذا الموضوع.",
|
||||
"markAsUnreadForAll.success": "تم تحديد الموضوع على أنه غير مقروء.",
|
||||
"mark_unread": "حدده كغير مقروء",
|
||||
"mark_unread.success": "الموضوع حدد على أنه غير مقروء",
|
||||
"mark_unread": "Mark unread",
|
||||
"mark_unread.success": "Topic marked as unread.",
|
||||
"watch": "مراقبة",
|
||||
"unwatch": "الغاء المراقبة",
|
||||
"watch.title": "استلم تنبيها بالردود الجديدة في هذا الموضوع",
|
||||
"unwatch.title": "ألغ مراقبة هذا الموضوع",
|
||||
"share_this_post": "انشر هذا الموضوع",
|
||||
"watching": "مراقبة",
|
||||
"not-watching": "غير مراقب",
|
||||
"ignoring": "تجاهل",
|
||||
"watching.description": "بلغني بالردود الجديدة<br/>\nاظهر الموضوع في غير مقروء",
|
||||
"watching": "Watching",
|
||||
"not-watching": "Not Watching",
|
||||
"ignoring": "Ignoring",
|
||||
"watching.description": "Notify me of new replies.<br/>Show topic in unread.",
|
||||
"not-watching.description": "Do not notify me of new replies.<br/>Show topic in unread if category is not ignored.",
|
||||
"ignoring.description": "Do not notify me of new replies.<br/>Do not show topic in unread.",
|
||||
"thread_tools.title": "أدوات الموضوع",
|
||||
"thread_tools.markAsUnreadForAll": "Mark Unread For All",
|
||||
"thread_tools.markAsUnreadForAll": "علم غير مقروء",
|
||||
"thread_tools.pin": "علق الموضوع",
|
||||
"thread_tools.unpin": "إلغاء تعليق الموضوع",
|
||||
"thread_tools.lock": "أقفل الموضوع",
|
||||
"thread_tools.unlock": "إلغاء إقفال الموضوع",
|
||||
"thread_tools.move": "نقل الموضوع",
|
||||
"thread_tools.move_all": "نقل الكل",
|
||||
"thread_tools.select_category": "Select Category",
|
||||
"thread_tools.fork": "إنشاء فرع الموضوع",
|
||||
"thread_tools.delete": "حذف الموضوع",
|
||||
"thread_tools.delete-posts": "مشاركات محذوفة",
|
||||
"thread_tools.delete-posts": "Delete Posts",
|
||||
"thread_tools.delete_confirm": "هل أنت متأكد أنك تريد حذف هذا الموضوع؟",
|
||||
"thread_tools.restore": "استعادة الموضوع",
|
||||
"thread_tools.restore_confirm": "هل أنت متأكد أنك تريد استعادة هذا الموضوع؟",
|
||||
"thread_tools.purge": "تطهير الموضوع",
|
||||
"thread_tools.purge_confirm": "هل أنت متأكد أنك تريد تطهير هذا الموضوع؟",
|
||||
"thread_tools.merge_topics": "Merge Topics",
|
||||
"thread_tools.merge": "Merge",
|
||||
"topic_move_success": "تم نقل هذا الموضوع إلى %1 بنجاح",
|
||||
"post_delete_confirm": "هل أنت متأكد أنك تريد حذف هذه المشاركة؟",
|
||||
"post_restore_confirm": "هل أنت متأكد أنك تريد استعادة هذه المشاركة؟",
|
||||
"post_purge_confirm": "هل أنت متأكد أنك تريد تطهير هذه المشاركة؟",
|
||||
"load_categories": "تحميل الفئات",
|
||||
"disabled_categories_note": "الفئات المعطلة رمادية",
|
||||
"confirm_move": "انقل",
|
||||
"confirm_fork": "فرع",
|
||||
"bookmark": "Bookmark",
|
||||
"bookmarks": "Bookmarks",
|
||||
"bookmarks.has_no_bookmarks": "You haven't bookmarked any posts yet.",
|
||||
"favourite": "Bookmark",
|
||||
"favourites": "Bookmarks",
|
||||
"favourites.has_no_favourites": "You haven't bookmarked any posts yet.",
|
||||
"loading_more_posts": "تحميل المزيد من المشاركات",
|
||||
"move_topic": "نقل الموضوع",
|
||||
"move_topics": "نقل المواضيع",
|
||||
"move_post": "نقل المشاركة",
|
||||
"post_moved": "تم نقل المشاركة",
|
||||
"fork_topic": "فرع الموضوع",
|
||||
"topic_will_be_moved_to": "هذا الموضوع سوف ينقل إلى فئة",
|
||||
"fork_topic_instruction": "إضغط على المشاركات التي تريد تفريعها",
|
||||
"fork_no_pids": "لم تختر أي مشاركة",
|
||||
"fork_pid_count": "1% مشاركة محددة",
|
||||
"fork_pid_count": "%1 post(s) selected",
|
||||
"fork_success": "تم إنشاء فرع للموضوع بنجاح! إضغط هنا لمعاينة الفرع.",
|
||||
"delete_posts_instruction": "Click the posts you want to delete/purge",
|
||||
"merge_topics_instruction": "Click the topics you want to merge",
|
||||
"composer.title_placeholder": "أدخل عنوان موضوعك هنا...",
|
||||
"composer.handle_placeholder": "اﻹسم",
|
||||
"composer.discard": "نبذ التغييرات",
|
||||
@@ -120,10 +113,10 @@
|
||||
"most_posts": "اﻷكثر رداً",
|
||||
"stale.title": "Create new topic instead?",
|
||||
"stale.warning": "The topic you are replying to is quite old. Would you like to create a new topic instead, and reference this one in your reply?",
|
||||
"stale.create": "موضوع جديد",
|
||||
"stale.reply_anyway": "الرد على هذا الموضوع ",
|
||||
"link_back": "رد: [%1](%2)",
|
||||
"diffs.title": "Post Edit History",
|
||||
"diffs.description": "This post has <strong>%1</strong> revisions. Click one of the revisions below to see the post content at that point in time.",
|
||||
"diffs.no-revisions-description": "This post has <strong>%1</strong> revisions."
|
||||
"stale.create": "Create a new topic",
|
||||
"stale.reply_anyway": "Reply to this topic anyway",
|
||||
"link_back": "Re: [%1](%2)",
|
||||
"spam": "Spam",
|
||||
"offensive": "Offensive",
|
||||
"custom-flag-reason": "Enter a flagging reason"
|
||||
}
|
||||
@@ -9,7 +9,5 @@
|
||||
"topics_marked_as_read.success": "تم تحديد المواضيع على أنها مقروءة!",
|
||||
"all-topics": "كل المواضيع",
|
||||
"new-topics": "مواضيع جديدة",
|
||||
"watched-topics": "المواضيع المتابعة",
|
||||
"unreplied-topics": "Unreplied Topics",
|
||||
"multiple-categories-selected": "Multiple Selected"
|
||||
"watched-topics": "المواضيع المتابعة"
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"uploading-file": "جاري رفع الملف...",
|
||||
"select-file-to-upload": "إختر ملف لرفعه!",
|
||||
"select-file-to-upload": "Select a file to upload!",
|
||||
"upload-success": "تم رفع الملف بنجاح!",
|
||||
"maximum-file-size": "الحجم الأقصى %1 كيلوبت"
|
||||
"maximum-file-size": "Maximum %1 kb"
|
||||
}
|
||||