mirror of
				https://github.com/NodeBB/NodeBB.git
				synced 2025-11-03 20:45:58 +01:00 
			
		
		
		
	Merge remote-tracking branch 'refs/remotes/origin/master' into develop
This commit is contained in:
		@@ -56,7 +56,7 @@ NodeBB requires the following software to be installed:
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
[Please refer to platform-specific installation documentation](http://docs.nodebb.org/en/latest/installing/os.html)
 | 
			
		||||
[Please refer to platform-specific installation documentation](https://docs.nodebb.org/installing/os)
 | 
			
		||||
 | 
			
		||||
## Securing NodeBB
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										5
									
								
								app.js
									
									
									
									
									
								
							
							
						
						
									
										5
									
								
								app.js
									
									
									
									
									
								
							@@ -26,7 +26,10 @@ if (require.main !== module) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var nconf = require('nconf');
 | 
			
		||||
nconf.argv().env('__');
 | 
			
		||||
nconf.argv().env({
 | 
			
		||||
	separator: '__',
 | 
			
		||||
	lowerCase: true,
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
var url = require('url');
 | 
			
		||||
var async = require('async');
 | 
			
		||||
 
 | 
			
		||||
@@ -142,7 +142,7 @@ 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') || urlObject.port || 4567;
 | 
			
		||||
	if (!Array.isArray(port)) {
 | 
			
		||||
		port = [port];
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -55,12 +55,12 @@
 | 
			
		||||
    "morgan": "^1.3.2",
 | 
			
		||||
    "mousetrap": "^1.5.3",
 | 
			
		||||
    "nconf": "~0.8.2",
 | 
			
		||||
    "nodebb-plugin-composer-default": "4.4.14",
 | 
			
		||||
    "nodebb-plugin-composer-default": "4.4.15",
 | 
			
		||||
    "nodebb-plugin-dbsearch": "2.0.4",
 | 
			
		||||
    "nodebb-plugin-emoji-extended": "1.1.1",
 | 
			
		||||
    "nodebb-plugin-emoji-one": "1.2.1",
 | 
			
		||||
    "nodebb-plugin-markdown": "7.1.1",
 | 
			
		||||
    "nodebb-plugin-mentions": "2.0.3",
 | 
			
		||||
    "nodebb-plugin-mentions": "2.1.1",
 | 
			
		||||
    "nodebb-plugin-soundpack-default": "1.0.0",
 | 
			
		||||
    "nodebb-plugin-spam-be-gone": "0.5.0",
 | 
			
		||||
    "nodebb-rewards-essentials": "0.0.9",
 | 
			
		||||
 
 | 
			
		||||
@@ -20,12 +20,12 @@
 | 
			
		||||
	"stats.all": "全て",
 | 
			
		||||
	
 | 
			
		||||
	"updates": "更新",
 | 
			
		||||
	"running-version": "<strong>NodeBB v <span id = \"version\">%1 </ span> </ strong>を実行しています。",
 | 
			
		||||
	"running-version": "<strong>NodeBB v<span id=\"version\">%1</span></strong> を実行しています。",
 | 
			
		||||
	"keep-updated": "常に最新のセキュリティパッチとバグ修正のためにNodeBBが最新であることを確認してください。",
 | 
			
		||||
	"up-to-date": "<p>あなたは<strong>最新の状態</ strong>です。<i class = \"fa fa-check\"> </ i> </ p>",
 | 
			
		||||
	"up-to-date": "<p>あなたは<strong>最新の状態</strong>です。<i class=\"fa fa-check\"></i></p>",
 | 
			
		||||
	"upgrade-available": "<p>新しいバージョン (v%1) がリリースされました。<a href=\"https://docs.nodebb.org/en/latest/upgrading/index.html\">NodeBBのアップグレード</a>を検討してください。</p>",
 | 
			
		||||
	"prerelease-upgrade-available": "<p>これはNodeBBの旧リリースのバージョンです。新しいバージョン(v%1)がリリースされました。<a href=\"https://docs.nodebb.org/en/latest/upgrading/index.html\"> NodeBBのアップグレード</a>を検討してください。</ p>",
 | 
			
		||||
	"prerelease-warning": "<p>これはNodeBBの<strong>プレリリース版</ strong>です。意図しないバグが発生することがあります。<i class=\"fa fa-exclamation-triangle\"></i></p>",
 | 
			
		||||
	"prerelease-warning": "<p>これはNodeBBの<strong>プレリリース版</strong>です。意図しないバグが発生することがあります。<i class=\"fa fa-exclamation-triangle\"></i></p>",
 | 
			
		||||
	"running-in-development": "<span>フォーラムが開発モードで動作しています。フォーラムの動作が脆弱かもしれませんので、管理者に問い合わせてください。</span>",
 | 
			
		||||
 | 
			
		||||
	"notices": "通知",
 | 
			
		||||
 
 | 
			
		||||
@@ -4,21 +4,21 @@
 | 
			
		||||
    "not-logged-in": "로그인하지 않았습니다.",
 | 
			
		||||
    "account-locked": "임시로 잠긴 계정입니다.",
 | 
			
		||||
    "search-requires-login": "검색을 하기 위해서는 계정이 필요합니다. 로그인하거나 가입해 주십시오.",
 | 
			
		||||
    "invalid-cid": "올바르지 않은 카테고리 ID입니다.",
 | 
			
		||||
    "invalid-tid": "올바르지 않은 주제 ID입니다.",
 | 
			
		||||
    "invalid-pid": "올바르지 않은 게시물 ID입니다.",
 | 
			
		||||
    "invalid-cid": "올바르지 않은 게시판 ID입니다.",
 | 
			
		||||
    "invalid-tid": "올바르지 않은 게시물 ID입니다.",
 | 
			
		||||
    "invalid-pid": "올바르지 않은 포스트 ID입니다.",
 | 
			
		||||
    "invalid-uid": "올바르지 않은 사용자 ID입니다.",
 | 
			
		||||
    "invalid-username": "올바르지 않은 사용자 이름입니다.",
 | 
			
		||||
    "invalid-username": "올바르지 않은 사용자명 입니다.",
 | 
			
		||||
    "invalid-email": "올바르지 않은 이메일입니다.",
 | 
			
		||||
    "invalid-title": "올바르지 않은 제목입니다.",
 | 
			
		||||
    "invalid-user-data": "올바르지 않은 사용자 정보입니다.",
 | 
			
		||||
    "invalid-password": "올바르지 않은 비밀번호입니다.",
 | 
			
		||||
    "invalid-login-credentials": "잘못된 로그인 정보입니다.",
 | 
			
		||||
    "invalid-username-or-password": "사용자 이름과 패스워드를 모두 설정해주세요.",
 | 
			
		||||
    "invalid-login-credentials": "올바르지 않은 로그인 정보입니다.",
 | 
			
		||||
    "invalid-username-or-password": "사용자명과 패스워드를 모두 설정해주세요.",
 | 
			
		||||
    "invalid-search-term": "올바르지 않은 검색어입니다.",
 | 
			
		||||
    "csrf-invalid": "세션이 만료되어 로그인에 실패하였습니다. 다시 시도해 주세요.",
 | 
			
		||||
    "invalid-pagination-value": "올바르지 않은 값입니다. 최소 1%에서 최대 2%까지 설정해야 합니다.",
 | 
			
		||||
    "username-taken": "이미 사용 중인 사용자 이름입니다.",
 | 
			
		||||
    "invalid-pagination-value": "올바르지 않은 페이지 값입니다. 최소 1% 에서 최대 2% 사이로 설정해야 합니다.",
 | 
			
		||||
    "username-taken": "이미 사용 중인 사용자명 입니다.",
 | 
			
		||||
    "email-taken": "이미 사용 중인 이메일입니다.",
 | 
			
		||||
    "email-not-confirmed": "아직 이메일이 인증되지 않았습니다. 여기를 누르면 인증 메일을 발송할 수 있습니다.",
 | 
			
		||||
    "email-not-confirmed-chat": "아직 이메일이 인증되지 않았습니다. 대화기능은 인증 후에 사용이 가능합니다.",
 | 
			
		||||
@@ -36,49 +36,49 @@
 | 
			
		||||
    "user-too-new": "죄송합니다, 첫 번째 게시물은 %1 초 후에 작성할 수 있습니다.",
 | 
			
		||||
    "blacklisted-ip": "죄송하지만, 당신의 IP는 이 커뮤니티로부터 차단되었습니다. 만약 에러라는 생각이 드신다면 관리자에게 연락해주세요.",
 | 
			
		||||
    "ban-expiry-missing": "해당 차단의 만료일을 설정 해주세요.",
 | 
			
		||||
    "no-category": "존재하지 않는 카테고리입니다.",
 | 
			
		||||
    "no-topic": "존재하지 않는 주제입니다.",
 | 
			
		||||
    "no-post": "존재하지 않는 게시물입니다.",
 | 
			
		||||
    "no-group": "존재하지 않는 그룹입니다.",
 | 
			
		||||
    "no-user": "존재하지 않는 사용자입니다.",
 | 
			
		||||
    "no-teaser": "존재하지 않는 미리보기입니다.",
 | 
			
		||||
    "no-category": "존재하지 않는 게시판 입니다.",
 | 
			
		||||
    "no-topic": "존재하지 않는 게시물 입니다.",
 | 
			
		||||
    "no-post": "존재하지 않는 포스트 입니다.",
 | 
			
		||||
    "no-group": "존재하지 않는 그룹 입니다.",
 | 
			
		||||
    "no-user": "존재하지 않는 사용자 입니다.",
 | 
			
		||||
    "no-teaser": "존재하지 않는 미리보기 입니다.",
 | 
			
		||||
    "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자 이내로 작성할 수 있습니다.",
 | 
			
		||||
    "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": "선택된 게시판이 없습니다.",
 | 
			
		||||
    "too-many-posts": "새 게시물 작성은 %1초마다 가능합니다 - 조금 천천히 작성해주세요.",
 | 
			
		||||
    "too-many-posts-newbie": "신규 사용자는 %2 만큼의 인지도를 얻기 전까지 %1초마다 게시물을 작성할 수 있습니다.  조금 천천히 작성해주세요.",
 | 
			
		||||
    "tag-too-short": "꼬리표가 너무 짧습니다. 꼬리표는 최소 %1자 이상이어야 합니다.",
 | 
			
		||||
    "tag-too-long": "꼬리표가 너무 깁니다. 꼬리표는 최대 %1자 이내로 사용가능합니다.",
 | 
			
		||||
    "not-enough-tags": "꼬리표가 없거나 부족합니다. 게시물은 %1개 이상의 꼬리표를 사용해야 합니다.",
 | 
			
		||||
    "too-many-tags": "꼬리표가 너무 많습니다. 게시물은 %1개 이하의 꼬리표를 사용할 수 있습니다.",
 | 
			
		||||
    "tag-too-short": "태그가 너무 짧습니다. 태그는 최소 %1자 이상이어야 합니다.",
 | 
			
		||||
    "tag-too-long": "태그가 너무 깁니다. 태그는 최대 %1자 이내로 사용가능합니다.",
 | 
			
		||||
    "not-enough-tags": "태그가 없거나 부족합니다. 게시물은 %1개 이상의 태그를 사용해야 합니다.",
 | 
			
		||||
    "too-many-tags": "태그가 너무 많습니다. 게시물은 %1개 이하의 태그를 사용할 수 있습니다.",
 | 
			
		||||
    "still-uploading": "업로드가 끝날 때까지 기다려주세요.",
 | 
			
		||||
    "file-too-big": "업로드 가능한 파일크기는 최대 %1 KB 입니다 - 파일의 용량을 줄이거나 압축을 활용하세요.",
 | 
			
		||||
    "guest-upload-disabled": "손님의 파일 업로드는 제한되어 있습니다.",
 | 
			
		||||
    "already-bookmarked": "이미 즐겨찾기에 추가된 글입니다.",
 | 
			
		||||
    "already-unbookmarked": "이미 즐겨찾기를 해제한 글입니다.",
 | 
			
		||||
    "guest-upload-disabled": "미가입 사용자의 파일 업로드는 제한되어 있습니다.",
 | 
			
		||||
    "already-bookmarked": "이미 즐겨찾기에 추가한 포스트 입니다.",
 | 
			
		||||
    "already-unbookmarked": "이미 즐겨찾기를 해제한 포스트 입니다.",
 | 
			
		||||
    "cant-ban-other-admins": "다른 관리자를 차단할 수 없습니다.",
 | 
			
		||||
    "cant-remove-last-admin": "귀하는 유일한 관리자입니다. 관리자를 그만두시기 전에 다른 사용자를 관리자로 선임하세요.",
 | 
			
		||||
    "cant-remove-last-admin": "귀하는 유일한 관리자입니다. 관리자를 그만두시기 전에 다른 사용자를 관리자로 임명하세요.",
 | 
			
		||||
    "cant-delete-admin": "해당 계정을 삭제하기 전에 관리자 권한을 해제 해주십시오.",
 | 
			
		||||
    "invalid-image-type": "올바르지 않은 이미지입니다. 사용가능한 유형: %1",
 | 
			
		||||
    "invalid-image-extension": "올바르지 않은 이미지 확장자입니다.",
 | 
			
		||||
@@ -86,43 +86,43 @@
 | 
			
		||||
    "group-name-too-short": "그룹 이름이 너무 짧습니다.",
 | 
			
		||||
    "group-name-too-long": "그룹 이름이 너무 깁니다.",
 | 
			
		||||
    "group-already-exists": "이미 존재하는 그룹입니다.",
 | 
			
		||||
    "group-name-change-not-allowed": "그룹 이름의 변경은 불가합니다.",
 | 
			
		||||
    "group-name-change-not-allowed": "그룹 이름의 변경이 불가능 합니다.",
 | 
			
		||||
    "group-already-member": "이미 이 그룹에 속해있습니다.",
 | 
			
		||||
    "group-not-member": "이 그룹의 구성원이 아닙니다.",
 | 
			
		||||
    "group-needs-owner": "이 그룹은 적어도 한 명의 소유자가 필요합니다.",
 | 
			
		||||
    "group-already-invited": "이 사용자는 이미 초대됐습니다.",
 | 
			
		||||
    "group-already-requested": "회원 요청이 이미 제출되었습니다.",
 | 
			
		||||
    "post-already-deleted": "이미 삭제된 게시물입니다.",
 | 
			
		||||
    "post-already-restored": "이미 복원된 게시물입니다.",
 | 
			
		||||
    "topic-already-deleted": "이미 삭제된 주제입니다.",
 | 
			
		||||
    "topic-already-restored": "이미 복원된 주제입니다.",
 | 
			
		||||
    "cant-purge-main-post": "주요 게시물은 삭제할 수 없습니다. 대신 주제를 삭제하세요.",
 | 
			
		||||
    "topic-thumbnails-are-disabled": "주제 섬네일이 이미 해제되었습니다.",
 | 
			
		||||
    "post-already-deleted": "이미 삭제된 포스트 입니다.",
 | 
			
		||||
    "post-already-restored": "이미 복원된 포스트 입니다.",
 | 
			
		||||
    "topic-already-deleted": "이미 삭제된 게시물 입니다.",
 | 
			
		||||
    "topic-already-restored": "이미 복원된 게시물 입니다.",
 | 
			
		||||
    "cant-purge-main-post": "메인 포스트는 삭제할 수 없습니다. 대신 게시물을 삭제하세요.",
 | 
			
		||||
    "topic-thumbnails-are-disabled": "게시물 섬네일이 비활성화 되었습니다.",
 | 
			
		||||
    "invalid-file": "올바르지 않은 파일입니다.",
 | 
			
		||||
    "uploads-are-disabled": "업로드는 비활성화되어 있습니다.",
 | 
			
		||||
    "signature-too-long": "서명은 %1자 이내로 작성할 수 있습니다.",
 | 
			
		||||
    "about-me-too-long": "자기소개는 %1자 이내로 작성할 수 있습니다.",
 | 
			
		||||
    "uploads-are-disabled": "업로드가 비활성화 되었습니다.",
 | 
			
		||||
    "signature-too-long": "서명은 %1 자를 넘길 수 없습니다.",
 | 
			
		||||
    "about-me-too-long": "자기소개는 %1 자를 넘길 수 없습니다.",
 | 
			
		||||
    "cant-chat-with-yourself": "자신과는 대화할 수 없습니다.",
 | 
			
		||||
    "chat-restricted": "이 사용자는 대화를 제한하고 있습니다. 대화하려면 이 사용자가 귀하를 따라야합니다.",
 | 
			
		||||
    "chat-disabled": "대화 기능을 사용하지 않습니다.",
 | 
			
		||||
    "chat-restricted": "이 사용자는 대화를 제한하고 있습니다. 대화하려면 이 사용자가 귀하를 팔로우 해야합니다.",
 | 
			
		||||
    "chat-disabled": "채팅 시스템이 비활성화 되었습니다.",
 | 
			
		||||
    "too-many-messages": "짧은 시간동안 너무 많은 메시지를 전송하였습니다. 잠시 후에 다시 시도하세요.",
 | 
			
		||||
    "invalid-chat-message": "올바르지 않은 메시지입니다.",
 | 
			
		||||
    "chat-message-too-long": "대화 메세지는 최대 %1자로 제한됩니다.",
 | 
			
		||||
    "cant-edit-chat-message": "편집 할 수 있는 권한이 없습니다.",
 | 
			
		||||
    "chat-message-too-long": "채팅 메세지는 최대 %1자로 제한됩니다.",
 | 
			
		||||
    "cant-edit-chat-message": "이 메세지를 수정 할 권한이 없습니다.",
 | 
			
		||||
    "cant-remove-last-user": "마지막 사용자를 삭제할 수 없습니다.",
 | 
			
		||||
    "cant-delete-chat-message": "메세지를 지울 권한이 없습니다.",
 | 
			
		||||
    "already-voting-for-this-post": "이미 이 게시글에 투표하셨습니다.",
 | 
			
		||||
    "reputation-system-disabled": "인지도 기능이 비활성 상태입니다.",
 | 
			
		||||
    "cant-delete-chat-message": "이 메세지를 삭제할 권한이 없습니다.",
 | 
			
		||||
    "already-voting-for-this-post": "이미 이 포스트에 투표하셨습니다.",
 | 
			
		||||
    "reputation-system-disabled": "인지도 시스템이 비활성 상태입니다.",
 | 
			
		||||
    "downvoting-disabled": "비추천 기능이 비활성 상태입니다.",
 | 
			
		||||
    "not-enough-reputation-to-downvote": "인지도가 낮아 이 게시물에 반대할 수 없습니다.",
 | 
			
		||||
    "not-enough-reputation-to-flag": "인지도가 낮아 이 게시물을 신고할 수 없습니다.",
 | 
			
		||||
    "not-enough-reputation-to-downvote": "인지도가 낮아 이 포스트를 비추천할 수 없습니다.",
 | 
			
		||||
    "not-enough-reputation-to-flag": "인지도가 낮아 이 포스트를 신고할 수 없습니다.",
 | 
			
		||||
    "already-flagged": "이미 이 게시물을 신고했습니다.",
 | 
			
		||||
    "reload-failed": "NodeBB 서버를 다시 읽어들이는 중 다음과 같은 문제가 발생했으나 사용자측은 지속적으로 자원을 제공받습니다. 오류 문구: \"%1\" 문제를 해결하시려면 다시 읽어들이기 전의 수정사항을 원래대로 되돌려주세요.  ",
 | 
			
		||||
    "registration-error": "등록 오류",
 | 
			
		||||
    "parse-error": "서버 응답을 파싱하는 동안 문제가 발생했습니다.",
 | 
			
		||||
    "parse-error": "서버로 부터의 응답을 읽는 동안 문제가 발생했습니다.",
 | 
			
		||||
    "wrong-login-type-email": "이메일 주소를 통해 로그인하세요.",
 | 
			
		||||
    "wrong-login-type-username": "사용자명을 통해 로그인하세요.",
 | 
			
		||||
    "invite-maximum-met": "초대가능한 사용자를 모두 초대했습니다. (%2명 중 %1을 초대)",
 | 
			
		||||
    "invite-maximum-met": "초대 한도 만큼의 사용자를 초대했습니다. (%2명 중 %1을 초대)",
 | 
			
		||||
    "no-session-found": "로그인 세션을 찾을 수 없습니다.",
 | 
			
		||||
    "not-in-room": "없는 사용자입니다.",
 | 
			
		||||
    "no-users-in-room": "사용자가 없습니다.",
 | 
			
		||||
 
 | 
			
		||||
@@ -7,21 +7,21 @@
 | 
			
		||||
	"assignee": "담당자",
 | 
			
		||||
	"update": "업데이트",
 | 
			
		||||
	"updated": "업데이트 되었습니다.",
 | 
			
		||||
	"target-purged": "해당 신고된 게시물은 완전 삭제 되었으며, 더이상 존재하지 않습니다.",
 | 
			
		||||
	"target-purged": "해당 신고된 컨텐츠는 완전 삭제 되었으며, 더이상 존재하지 않습니다.",
 | 
			
		||||
 | 
			
		||||
	"quick-filters": "간편 필터",
 | 
			
		||||
	"filter-active": "적용된 하나 이상의 필터가 있습니다.",
 | 
			
		||||
	"filter-reset": "필터 제거",
 | 
			
		||||
	"filters": "필터 항목",
 | 
			
		||||
	"filters": "필터 옵션",
 | 
			
		||||
	"filter-reporterId": "신고자 ID",
 | 
			
		||||
	"filter-targetUid": "신고된 게시물 ID",
 | 
			
		||||
	"filter-type": "신고 유형",
 | 
			
		||||
	"filter-type-all": "모든 컨텐츠",
 | 
			
		||||
	"filter-type-post": "글",
 | 
			
		||||
	"filter-type-post": "포스트",
 | 
			
		||||
	"filter-state": "처리 상태",
 | 
			
		||||
	"filter-assignee": "담당자 ID",
 | 
			
		||||
	"filter-cid": "게시판",
 | 
			
		||||
	"filter-quick-mine": "나에게 배정",
 | 
			
		||||
	"filter-quick-mine": "나에게 배정된 신고",
 | 
			
		||||
	"filter-cid-all": "모든 게시판",
 | 
			
		||||
	"apply-filters": "필터 적용",
 | 
			
		||||
 | 
			
		||||
@@ -53,7 +53,7 @@
 | 
			
		||||
	"modal-title": "부적절한 컨텐츠 신고",
 | 
			
		||||
	"modal-body": "%1 %2 에 대한 신고 사유를 적어주시거나, 빠른 신고 버튼 중 하나를 사용해 주세요.",
 | 
			
		||||
	"modal-reason-spam": "스팸",
 | 
			
		||||
	"modal-reason-offensive": "공격적인",
 | 
			
		||||
	"modal-reason-offensive": "부적절한 글",
 | 
			
		||||
	"modal-reason-custom": "신고 사유",
 | 
			
		||||
	"modal-submit": "리포트 제출",
 | 
			
		||||
	"modal-submit-success": "이 컨텐츠는 신고되었습니다."
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
    "search": "검색",
 | 
			
		||||
    "buttons.close": "닫기",
 | 
			
		||||
    "403.title": "접근이 거부되었습니다.",
 | 
			
		||||
    "403.message": "접속할 수 없는 페이지에 접근하였습니다.",
 | 
			
		||||
    "403.message": "권한이 없는 페이지에 접속을 시도하였습니다.",
 | 
			
		||||
    "403.login": "<a href='/login'>로그인</a>되어 있는지 확인해 주세요.",
 | 
			
		||||
    "404.title": "페이지를 찾을 수 없습니다.",
 | 
			
		||||
    "404.message": "존재하지 않는 페이지에 접근하였습니다. <a href='%1/'>홈 페이지</a>로 이동합니다.",
 | 
			
		||||
@@ -15,21 +15,21 @@
 | 
			
		||||
    "login": "로그인",
 | 
			
		||||
    "please_log_in": "로그인해 주세요.",
 | 
			
		||||
    "logout": "로그아웃",
 | 
			
		||||
    "posting_restriction_info": "게시물 작성은 현재 회원에게만 제한되고 있습니다. 여기를 누르면 로그인 페이지로 이동합니다.",
 | 
			
		||||
    "posting_restriction_info": "현재 회원들만 게시물을 작성 할 수 있습니다.여기를 누르면 로그인 페이지로 이동합니다.",
 | 
			
		||||
    "welcome_back": "환영합니다.",
 | 
			
		||||
    "you_have_successfully_logged_in": "성공적으로 로그인했습니다.",
 | 
			
		||||
    "save_changes": "저장",
 | 
			
		||||
    "save": "저장",
 | 
			
		||||
    "close": "닫기",
 | 
			
		||||
    "pagination": "페이지",
 | 
			
		||||
    "pagination.out_of": "%2개 중 %1개",
 | 
			
		||||
    "pagination.out_of": "%2 개 중 %1 개",
 | 
			
		||||
    "pagination.enter_index": "이동할 게시물 번호를 입력하세요.",
 | 
			
		||||
    "header.admin": "관리자",
 | 
			
		||||
    "header.categories": "카테고리",
 | 
			
		||||
    "header.recent": "최근 주제",
 | 
			
		||||
    "header.unread": "읽지 않은 주제",
 | 
			
		||||
    "header.tags": "꼬리표",
 | 
			
		||||
    "header.popular": "인기 주제",
 | 
			
		||||
    "header.categories": "게시판",
 | 
			
		||||
    "header.recent": "최근 게시물",
 | 
			
		||||
    "header.unread": "읽지 않은 게시물",
 | 
			
		||||
    "header.tags": "태그",
 | 
			
		||||
    "header.popular": "인기 게시물",
 | 
			
		||||
    "header.users": "사용자",
 | 
			
		||||
    "header.groups": "그룹",
 | 
			
		||||
    "header.chats": "채팅",
 | 
			
		||||
@@ -46,35 +46,35 @@
 | 
			
		||||
    "alert.error": "오류",
 | 
			
		||||
    "alert.banned": "차단됨",
 | 
			
		||||
    "alert.banned.message": "이 계정은 차단되었습니다. 지금 로그아웃됩니다.",
 | 
			
		||||
    "alert.unfollow": "더 이상 %1님을 팔로우하지 않습니다.",
 | 
			
		||||
    "alert.follow": "%1님을 팔로우합니다.",
 | 
			
		||||
    "alert.unfollow": "더 이상 %1 님을 팔로우 하지않습니다.",
 | 
			
		||||
    "alert.follow": "%1 님을 팔로우 합니다.",
 | 
			
		||||
    "online": "온라인",
 | 
			
		||||
    "users": "사용자",
 | 
			
		||||
    "topics": "주제",
 | 
			
		||||
    "posts": "게시물",
 | 
			
		||||
    "topics": "게시물",
 | 
			
		||||
    "posts": "포스트",
 | 
			
		||||
    "best": "베스트",
 | 
			
		||||
    "upvoters": "추천한 유저",
 | 
			
		||||
    "upvoted": "Upvoted",
 | 
			
		||||
    "downvoters": "비추천한 유저",
 | 
			
		||||
    "downvoted": "비추됨",
 | 
			
		||||
    "upvoters": "추천한 사용자",
 | 
			
		||||
    "upvoted": "추천된 게시물",
 | 
			
		||||
    "downvoters": "비추천한 사용자",
 | 
			
		||||
    "downvoted": "비추천된 게시물",
 | 
			
		||||
    "views": "조회 수",
 | 
			
		||||
    "reputation": "인기도",
 | 
			
		||||
    "read_more": "전체 보기",
 | 
			
		||||
    "reputation": "등급",
 | 
			
		||||
    "read_more": "더 보기",
 | 
			
		||||
    "more": "더 보기",
 | 
			
		||||
    "posted_ago_by_guest": "익명 사용자가 %1에 작성했습니다.",
 | 
			
		||||
    "posted_ago_by": "%2님이 %1에 작성했습니다.",
 | 
			
		||||
    "posted_ago": "%1에 작성되었습니다.",
 | 
			
		||||
    "posted_in": "%1에 작성되었습니다.",
 | 
			
		||||
    "posted_in_by": "%2님이 %1에 작성했습니다.",
 | 
			
		||||
    "posted_in_ago": "%2 %1에 작성되었습니다. ",
 | 
			
		||||
    "posted_in_ago_by": "%3님이 %2 %1에 작성했습니다.",
 | 
			
		||||
    "user_posted_ago": "%1님이 %2에 작성했습니다.",
 | 
			
		||||
    "guest_posted_ago": "익명 사용자가 %1에 작성했습니다.",
 | 
			
		||||
    "last_edited_by": "%1님이 마지막으로 수정했습니다.",
 | 
			
		||||
    "norecentposts": "최근 작성된 게시물이 없습니다.",
 | 
			
		||||
    "norecenttopics": "최근 생성된 생성된 주제가 없습니다.",
 | 
			
		||||
    "recentposts": "최근 게시물",
 | 
			
		||||
    "recentips": "최근 로그인 IP",
 | 
			
		||||
    "posted_ago_by_guest": "미가입 사용자가 %1 에 작성했습니다.",
 | 
			
		||||
    "posted_ago_by": "%2 님이 %1 에 작성했습니다.",
 | 
			
		||||
    "posted_ago": "%1 에 작성되었습니다.",
 | 
			
		||||
    "posted_in": "%1 에 작성되었습니다.",
 | 
			
		||||
    "posted_in_by": "%2 님이 %1 에 작성했습니다.",
 | 
			
		||||
    "posted_in_ago": "%2 %1 에 작성되었습니다. ",
 | 
			
		||||
    "posted_in_ago_by": "%3 님이 %2 %1 에 작성했습니다.",
 | 
			
		||||
    "user_posted_ago": "%1 님이 %2 에 작성했습니다.",
 | 
			
		||||
    "guest_posted_ago": "미가입 사용자가 %1 에 작성했습니다.",
 | 
			
		||||
    "last_edited_by": "%1 님이 마지막으로 수정했습니다.",
 | 
			
		||||
    "norecentposts": "최근 작성된 포스트가 없습니다.",
 | 
			
		||||
    "norecenttopics": "최근 작성된 게시물이 없습니다.",
 | 
			
		||||
    "recentposts": "최근 포스트",
 | 
			
		||||
    "recentips": "최근에 로그인한 IP",
 | 
			
		||||
    "moderator_tools": "(준)관리자 도구모음",
 | 
			
		||||
    "away": "자리 비움",
 | 
			
		||||
    "dnd": "방해 금지",
 | 
			
		||||
@@ -85,7 +85,7 @@
 | 
			
		||||
    "guest": "익명 사용자",
 | 
			
		||||
    "guests": "익명 사용자",
 | 
			
		||||
    "updated.title": "포럼이 업데이트 되었습니다.",
 | 
			
		||||
    "updated.message": "이 포럼은 지금 최신 버전으로 업데이트 되었습니다. 여기를 누르면 페이지를 새로고침합니다.",
 | 
			
		||||
    "updated.message": "이 포럼은 지금 최신 버전으로 업데이트 되었습니다. 페이지를 새로고침 하시려면 여기를 클릭해주세요.",
 | 
			
		||||
    "privacy": "개인정보",
 | 
			
		||||
    "follow": "팔로우",
 | 
			
		||||
    "unfollow": "언팔로우",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,28 +1,28 @@
 | 
			
		||||
{
 | 
			
		||||
    "groups": "그룹",
 | 
			
		||||
    "view_group": "그룹 보기",
 | 
			
		||||
    "owner": "그룹관리자",
 | 
			
		||||
    "new_group": "그룹 생성",
 | 
			
		||||
    "owner": "그룹 관리자",
 | 
			
		||||
    "new_group": "새로운 그룹 만들기",
 | 
			
		||||
    "no_groups_found": "그룹이 없습니다.",
 | 
			
		||||
    "pending.accept": "수락",
 | 
			
		||||
    "pending.reject": "거절",
 | 
			
		||||
    "pending.accept_all": "전체 수락",
 | 
			
		||||
    "pending.reject_all": "전체 거절",
 | 
			
		||||
    "pending.none": "지금은 승인대기 회원이 없습니다.",
 | 
			
		||||
    "pending.none": "지금은 승인 대기중인 회원이 없습니다.",
 | 
			
		||||
    "invited.none": "지금은 초대된 회원이 없습니다.",
 | 
			
		||||
    "invited.uninvite": "초대를 철회",
 | 
			
		||||
    "invited.search": "그룹에 초대할 사용자를 검색하세요.",
 | 
			
		||||
    "invited.uninvite": "초대 취소",
 | 
			
		||||
    "invited.search": "그룹에 초대할 사용자 검색",
 | 
			
		||||
    "invited.notification_title": "<strong>%1</strong> 그룹에 초대되었습니다.",
 | 
			
		||||
    "request.notification_title": "<strong>%1</strong> 님으로부터 그룹 구성원 요청이 들어왔습니다.",
 | 
			
		||||
    "request.notification_text": "<strong>%1</strong> 님이 <strong>%2</strong> 의 멤버 가입을 신청했습니다.",
 | 
			
		||||
    "request.notification_title": "<strong>%1</strong> 님으로부터 그룹 가입 요청이 들어왔습니다.",
 | 
			
		||||
    "request.notification_text": "<strong>%1</strong> 님이 <strong>%2</strong> 에 가입을 신청했습니다.",
 | 
			
		||||
    "cover-save": "저장",
 | 
			
		||||
    "cover-saving": "저장 중",
 | 
			
		||||
    "details.title": "그룹 상세정보",
 | 
			
		||||
    "details.members": "구성원 목록",
 | 
			
		||||
    "details.pending": "대기 구성원",
 | 
			
		||||
    "details.pending": "승인 대기중인 구성원",
 | 
			
		||||
    "details.invited": "초대된 구성원",
 | 
			
		||||
    "details.has_no_posts": "이 그룹의 구성원이 작성한 게시물이 없습니다.",
 | 
			
		||||
    "details.latest_posts": "최근 게시물",
 | 
			
		||||
    "details.latest_posts": "최근 포스트",
 | 
			
		||||
    "details.private": "비공개",
 | 
			
		||||
    "details.disableJoinRequests": "가입 신청 비활성화하기",
 | 
			
		||||
    "details.grant": "소유권 이전/포기하기",
 | 
			
		||||
@@ -38,7 +38,7 @@
 | 
			
		||||
    "details.change_colour": "컬러 변경",
 | 
			
		||||
    "details.badge_text": "배지 문구",
 | 
			
		||||
    "details.userTitleEnabled": "배지 보이기",
 | 
			
		||||
    "details.private_help": "활성 후 구성원 가입시 그룹 관리자의 승인이 필요합니다.",
 | 
			
		||||
    "details.private_help": "활성 시, 구성원 가입시 그룹 관리자의 승인이 필요합니다.",
 | 
			
		||||
    "details.hidden": "숨김",
 | 
			
		||||
    "details.hidden_help": "활성 시 그룹 목록에 노출되지 않습니다. 또한 구성원은 초대를 통해서만 가입이 가능합니다.",
 | 
			
		||||
    "details.delete_group": "그룹 삭제",
 | 
			
		||||
@@ -51,7 +51,7 @@
 | 
			
		||||
    "membership.leave-group": "그룹 나가기",
 | 
			
		||||
    "membership.reject": "거절",
 | 
			
		||||
    "new-group.group_name": "그룹명:",
 | 
			
		||||
    "upload-group-cover": "그룹 커버 업로드",
 | 
			
		||||
    "upload-group-cover": "그룹 커버 사진 업로드",
 | 
			
		||||
    "bulk-invite-instructions": "초대하고자 하는 사용자 목록을 콤마(,) 로 구분하여 기입해 주십시오.\n예:  bravominski, stjohndlee, yoojkim",
 | 
			
		||||
    "bulk-invite": "다수의 사용자 초대",
 | 
			
		||||
    "remove_group_cover_confirm": "해당 커버 사진을 제거 하시겠습니까?"
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
{
 | 
			
		||||
    "username-email": "사용자 이름 / 이메일",
 | 
			
		||||
    "username-email": "사용자명 / 이메일",
 | 
			
		||||
    "username": "사용자명",
 | 
			
		||||
    "email": "이메일",
 | 
			
		||||
    "remember_me": "로그인 유지",
 | 
			
		||||
    "forgot_password": "비밀번호 초기화",
 | 
			
		||||
    "forgot_password": "비밀번호 재설정",
 | 
			
		||||
    "alternative_logins": "다른 방법으로 로그인",
 | 
			
		||||
    "failed_login_attempt": "로그인 실패",
 | 
			
		||||
    "login_successful": "성공적으로 로그인했습니다.",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,33 +1,33 @@
 | 
			
		||||
{
 | 
			
		||||
    "chat.chatting_with": "<span id=\"chat-with-name\">",
 | 
			
		||||
    "chat.chatting_with": "<span id=\"chat-with-name\"></span> 님과의 채팅",
 | 
			
		||||
    "chat.placeholder": "메시지를 여기에 입력한 후 엔터를 눌러 전송하세요.",
 | 
			
		||||
    "chat.send": "전송",
 | 
			
		||||
    "chat.no_active": "활성화된 채팅이 없습니다.",
 | 
			
		||||
    "chat.user_typing": "%1님이 입력 중입니다.",
 | 
			
		||||
    "chat.user_has_messaged_you": "%1님이 메시지를 보냈습니다.",
 | 
			
		||||
    "chat.see_all": "모든 대화 보기",
 | 
			
		||||
    "chat.mark_all_read": "읽은 채팅 읽음으로 표시",
 | 
			
		||||
    "chat.no-messages": "대화 기록을 보려면 대화 상대를 선택하세요.",
 | 
			
		||||
    "chat.user_typing": "%1 님이 입력 중입니다.",
 | 
			
		||||
    "chat.user_has_messaged_you": "%1 님이 메시지를 보냈습니다.",
 | 
			
		||||
    "chat.see_all": "모든 채팅 보기",
 | 
			
		||||
    "chat.mark_all_read": "모든 채팅 읽음으로 표시",
 | 
			
		||||
    "chat.no-messages": "채팅 기록을 보려면 채팅 상대를 선택하세요.",
 | 
			
		||||
    "chat.no-users-in-room": "사용자가 없습니다.",
 | 
			
		||||
    "chat.recent-chats": "최근 대화 내용",
 | 
			
		||||
    "chat.recent-chats": "최근 채팅",
 | 
			
		||||
    "chat.contacts": "연락처",
 | 
			
		||||
    "chat.message-history": "대화 기록",
 | 
			
		||||
    "chat.pop-out": "분리된 창에서 대화",
 | 
			
		||||
    "chat.pop-out": "분리된 창에서 채팅",
 | 
			
		||||
    "chat.minimize": "최소화",
 | 
			
		||||
    "chat.maximize": "최대화",
 | 
			
		||||
    "chat.seven_days": "7일",
 | 
			
		||||
    "chat.thirty_days": "30일",
 | 
			
		||||
    "chat.three_months": "3개월",
 | 
			
		||||
    "chat.delete_message_confirm": "이 대화를 삭제하시겠습니까?",
 | 
			
		||||
    "chat.add-users-to-room": "유저 추가하기",
 | 
			
		||||
    "chat.delete_message_confirm": "이 메세지를 삭제 하시겠습니까?",
 | 
			
		||||
    "chat.add-users-to-room": "사용자 추가하기",
 | 
			
		||||
    "chat.confirm-chat-with-dnd-user": "이 사용자의 상태는 \"방해금지\" 입니다. 그래도 대화를 요청 하시겠습니까?",
 | 
			
		||||
    "composer.compose": "작성",
 | 
			
		||||
    "composer.show_preview": "미리보기",
 | 
			
		||||
    "composer.hide_preview": "미리보기 숨김",
 | 
			
		||||
    "composer.user_said_in": "%1님이 %2에서 한 말:",
 | 
			
		||||
    "composer.user_said": "%1님의 말:",
 | 
			
		||||
    "composer.discard": "이 게시물을 지우겠습니까?",
 | 
			
		||||
    "composer.submit_and_lock": "잠금 상태로 작성 완료",
 | 
			
		||||
    "composer.user_said_in": "%1 님이 %2 에서 보낸 메세지:",
 | 
			
		||||
    "composer.user_said": "%1 님의 메세지:",
 | 
			
		||||
    "composer.discard": "이 포스트를 삭제 하시겠습니까?",
 | 
			
		||||
    "composer.submit_and_lock": "글 작성 후 잠금",
 | 
			
		||||
    "composer.toggle_dropdown": "내려서 확인하기",
 | 
			
		||||
    "composer.uploading": "%1 업로드 중",
 | 
			
		||||
    "composer.formatting.bold": "굵은글씨",
 | 
			
		||||
 
 | 
			
		||||
@@ -3,46 +3,46 @@
 | 
			
		||||
    "no_notifs": "새 알림이 없습니다.",
 | 
			
		||||
    "see_all": "모든 알림 보기",
 | 
			
		||||
    "mark_all_read": "모든 알림을 읽음 상태로 변경",
 | 
			
		||||
    "back_to_home": "%1로 돌아가기",
 | 
			
		||||
    "back_to_home": "%1 로 돌아가기",
 | 
			
		||||
    "outgoing_link": "외부 링크",
 | 
			
		||||
    "outgoing_link_message": "%1 에서 나오셨습니다.",
 | 
			
		||||
    "outgoing_link_message": "%1 를 떠납니다.",
 | 
			
		||||
    "continue_to": "%1 사이트로 이동",
 | 
			
		||||
    "return_to": "%1 사이트로 돌아가기",
 | 
			
		||||
    "new_notification": "새 알림",
 | 
			
		||||
    "you_have_unread_notifications": "읽지 않은 알림이 있습니다.",
 | 
			
		||||
    "all": "모든 알림",
 | 
			
		||||
    "topics": "토픽",
 | 
			
		||||
    "topics": "게시물",
 | 
			
		||||
    "replies": "답글",
 | 
			
		||||
    "chat": "대화",
 | 
			
		||||
    "chat": "채팅",
 | 
			
		||||
    "follows": "팔로우",
 | 
			
		||||
    "upvote": "추천",
 | 
			
		||||
    "new-flags": "새로 들어온 신고",
 | 
			
		||||
    "my-flags": "내게 배정된 신고",
 | 
			
		||||
    "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>님과 <strong>%2</strong>님이 <strong>%3</strong>의 내 게시물을 추천했습니다.",
 | 
			
		||||
    "upvoted_your_post_in_multiple": "<strong>%1</strong> 님과 다른 %2 명이 <strong>%3</strong>의 내 게시물을 추천했습니다.",
 | 
			
		||||
    "moved_your_post": "<strong>%1</strong>님이 귀하의 게시물을 <strong>%2</strong>로 옮겼습니다.",
 | 
			
		||||
    "moved_your_topic": "<strong>%1</strong> 이 <strong>%2</strong> 로 옮겨졌습니다.",
 | 
			
		||||
    "user_flagged_post_in": "<strong>%1</strong>님이 <strong>%2</strong>의 게시물을 신고했습니다.",
 | 
			
		||||
    "user_flagged_post_in_dual": "<strong>%1</strong> 님과 <strong>%2</strong> 님이 <strong>%3</strong> 안의 게시물에 플래그를 세웠습니다.",
 | 
			
		||||
    "user_flagged_post_in_multiple": "<strong>%1</strong> 님과 %2 명의 다른 유저들이 <strong>%3</strong> 안의 게시물에 플래그를 세웠습니다.",
 | 
			
		||||
    "upvoted_your_post_in": "<strong>%1</strong>님이 <strong>%2</strong>의 내 포스트를 추천했습니다.",
 | 
			
		||||
    "upvoted_your_post_in_dual": "<strong>%1</strong>님과 <strong>%2</strong>님이 <strong>%3</strong>의 내 포스트를 추천했습니다.",
 | 
			
		||||
    "upvoted_your_post_in_multiple": "<strong>%1</strong> 님과 다른 %2 명이 <strong>%3</strong>의 내 포스트를 추천했습니다.",
 | 
			
		||||
    "moved_your_post": "<strong>%1</strong>님이 귀하의 포스트를 <strong>%2</strong>로 옮겼습니다.",
 | 
			
		||||
    "moved_your_topic": "<strong>%1</strong> 이 <strong>%2</strong> 를 옮겼습니다.",
 | 
			
		||||
    "user_flagged_post_in": "<strong>%1</strong>님이 <strong>%2</strong>에 속한 포스트를 신고했습니다.",
 | 
			
		||||
    "user_flagged_post_in_dual": "<strong>%1</strong> 님과 <strong>%2</strong> 님이 <strong>%3</strong> 에 속한 포스트를 신고했습니다.",
 | 
			
		||||
    "user_flagged_post_in_multiple": "<strong>%1</strong> 님과 %2 명의 다른 유저들이 <strong>%3</strong> 에 속한 포스트를 신고했습니다.",
 | 
			
		||||
    "user_flagged_user": "<strong>%1</strong>님이 <strong>%2</strong>의 프로필을 신고했습니다.",
 | 
			
		||||
    "user_flagged_user_dual": "<strong>%1</strong>님과 <strong>%2</strong>님이 <strong>%3</strong>의 프로필을 신고했습니다.",
 | 
			
		||||
    "user_flagged_user_multiple": "<strong>%1</strong> 님과 다른 %2 명이 <strong>%3</strong>의 프로필을 신고했습니다.",
 | 
			
		||||
    "user_posted_to": "<strong>%1</strong>님이 <strong>%2</strong>에 답글을 작성했습니다.",
 | 
			
		||||
    "user_posted_to": "<strong>%1</strong>님이 <strong>%2</strong> 에 답글을 달았습니다.",
 | 
			
		||||
    "user_posted_to_dual": "<strong>%1</strong> 님과 <strong>%2</strong> 님이 <strong>%3</strong> 에 답글을 달았습니다.",
 | 
			
		||||
    "user_posted_to_multiple": "<strong>%1</strong> 님과 %2 명의 다른 유저들이 <strong>%3</strong> 에 답글을 달았습니다.",
 | 
			
		||||
    "user_posted_topic": "<strong>%1</strong>님이 새 주제를 작성했습니다: <strong>%2</strong>",
 | 
			
		||||
    "user_started_following_you": "<strong>%1</strong>님이 나를 팔로우합니다.",
 | 
			
		||||
    "user_started_following_you_dual": "<strong>%1</strong>님과 <strong>%2</strong>님이 당신을 팔로우 시작했습니다.",
 | 
			
		||||
    "user_started_following_you_multiple": "<strong>%1</strong>님외 %2명이 당신을 팔로우 시작했습니다.",
 | 
			
		||||
    "user_posted_topic": "<strong>%1</strong>님이 새 게시물을 작성했습니다: <strong>%2</strong>",
 | 
			
		||||
    "user_started_following_you": "<strong>%1</strong>님이 나를 팔로우 합니다.",
 | 
			
		||||
    "user_started_following_you_dual": "<strong>%1</strong>님과 <strong>%2</strong>님이 나를 팔로우 합니다.",
 | 
			
		||||
    "user_started_following_you_multiple": "<strong>%1</strong>님외 %2명이 나를 팔로우 합니다.",
 | 
			
		||||
    "new_register": "<strong>%1</strong>님이 가입요청을 했습니다.",
 | 
			
		||||
    "new_register_multiple": "<strong>%1<strong> 개의 회원가입 요청이 승인 대기중입니다.",
 | 
			
		||||
    "flag_assigned_to_you": "신고 ID <strong>%1</strong>  이 배정되었습니다.",
 | 
			
		||||
    "email-confirmed": "확인된 이메일",
 | 
			
		||||
    "email-confirmed-message": "이메일을 확인해주셔서 감사합니다. 계정이 완전히 활성화되었습니다.",
 | 
			
		||||
    "email-confirm-error-message": "이메일 주소를 검증하지 못했습니다. 코드가 올바르지 않거나 만료되었을 수 있습니다.",
 | 
			
		||||
    "new_register_multiple": "<strong>%1</strong> 개의 회원가입 요청이 승인 대기중입니다.",
 | 
			
		||||
    "flag_assigned_to_you": "<strong>신고 ID %1</strong> 이 나에게 배정되었습니다.",
 | 
			
		||||
    "email-confirmed": "이메일 인증 되었습니다",
 | 
			
		||||
    "email-confirmed-message": "이메일을 인증해주셔서 감사합니다. 계정이 완전히 활성화되었습니다.",
 | 
			
		||||
    "email-confirm-error-message": "이메일 주소를 인증하지 못했습니다. 코드가 올바르지 않거나 만료 되었을 수 있습니다.",
 | 
			
		||||
    "email-confirm-sent": "확인 이메일이 발송되었습니다."
 | 
			
		||||
}
 | 
			
		||||
@@ -1,51 +1,51 @@
 | 
			
		||||
{
 | 
			
		||||
    "home": "홈",
 | 
			
		||||
    "unread": "읽지 않은 주제",
 | 
			
		||||
    "popular-day": "인기있는 주제 (오늘)",
 | 
			
		||||
    "popular-week": "인기있는 주제 (주간)",
 | 
			
		||||
    "popular-month": "인기있는 주제 (월간)",
 | 
			
		||||
    "popular-alltime": "인기있는 주제",
 | 
			
		||||
    "recent": "최근 주제",
 | 
			
		||||
    "flagged-content": "플래그된 컨텐츠",
 | 
			
		||||
    "unread": "읽지 않은 게시물",
 | 
			
		||||
    "popular-day": "인기있는 게시물 (오늘)",
 | 
			
		||||
    "popular-week": "인기있는 게시물 (주간)",
 | 
			
		||||
    "popular-month": "인기있는 게시물 (월간)",
 | 
			
		||||
    "popular-alltime": "인기있는 게시물",
 | 
			
		||||
    "recent": "최근 게시물",
 | 
			
		||||
    "flagged-content": "신고된 컨텐츠",
 | 
			
		||||
    "ip-blacklist": "IP 블랙리스트",
 | 
			
		||||
    "users/online": "온라인 사용자",
 | 
			
		||||
    "users/online": "접속중인 사용자",
 | 
			
		||||
    "users/latest": "최근 사용자",
 | 
			
		||||
    "users/sort-posts": "가장 많은 게시물을 작성한 사용자",
 | 
			
		||||
    "users/sort-reputation": "가장 높은 인지도를 가진 사용자",
 | 
			
		||||
    "users/banned": "차단된 유저",
 | 
			
		||||
    "users/most-flags": "가장 많이 플래그가 달린 사용자",
 | 
			
		||||
    "users/sort-reputation": "가장 높은 등급을 가진 사용자",
 | 
			
		||||
    "users/banned": "차단된 사용자",
 | 
			
		||||
    "users/most-flags": "가장 많이 신고된 사용자",
 | 
			
		||||
    "users/search": "사용자 검색",
 | 
			
		||||
    "notifications": "알림",
 | 
			
		||||
    "tags": "태그",
 | 
			
		||||
    "tag": "\"%1\" 꼬리표가 달린 주제",
 | 
			
		||||
    "tag": "\"%1\" 태그가 달린 게시물",
 | 
			
		||||
    "register": "회원가입",
 | 
			
		||||
    "registration-complete": "회원가입 완료",
 | 
			
		||||
    "login": "로그인",
 | 
			
		||||
    "reset": "패스워드 초기화",
 | 
			
		||||
    "categories": "카테고리",
 | 
			
		||||
    "reset": "패스워드 재설정",
 | 
			
		||||
    "categories": "게시판",
 | 
			
		||||
    "groups": "그룹",
 | 
			
		||||
    "group": "%1 그룹",
 | 
			
		||||
    "chats": "대화",
 | 
			
		||||
    "chat": "%1 님과 대화",
 | 
			
		||||
    "chats": "채팅",
 | 
			
		||||
    "chat": "%1 님과 채팅",
 | 
			
		||||
    "flags": "신고 목록",
 | 
			
		||||
    "flag-details": "신고 ID %1 의 세부내용",
 | 
			
		||||
    "account/edit": "\"%1\" 편집",
 | 
			
		||||
    "account/edit/password": "\"%1\" 의 패스워드 변경",
 | 
			
		||||
    "account/edit/username": "\"%1\" 의 사용자명 변경",
 | 
			
		||||
    "account/edit/email": "\"%1\" 의 이메일 변경",
 | 
			
		||||
    "account/edit": "\"%1\" 수정",
 | 
			
		||||
    "account/edit/password": "\"%1\" 의 패스워드 수정",
 | 
			
		||||
    "account/edit/username": "\"%1\" 의 사용자명 수정",
 | 
			
		||||
    "account/edit/email": "\"%1\" 의 이메일 수정",
 | 
			
		||||
    "account/info": "계정 정보",
 | 
			
		||||
    "account/following": "%1 명이 팔로우",
 | 
			
		||||
    "account/followers": "%1 명을 팔로우",
 | 
			
		||||
    "account/posts": "%1 님이 작성한 게시물",
 | 
			
		||||
    "account/topics": "%1 님이 생성한 주제",
 | 
			
		||||
    "account/groups": "%1님의 그룹",
 | 
			
		||||
    "account/bookmarks": "%1 님이 즐겨찾기 한 게시물",
 | 
			
		||||
    "account/following": "%1 님이 팔로우 하는 사용자",
 | 
			
		||||
    "account/followers": "%1 님을 팔로우 하는 사용자",
 | 
			
		||||
    "account/posts": "%1 님이 작성한 포스트",
 | 
			
		||||
    "account/topics": "%1 님이 생성한 게시물",
 | 
			
		||||
    "account/groups": "%1 님의 그룹",
 | 
			
		||||
    "account/bookmarks": "%1 님이 즐겨찾기 한 포스트",
 | 
			
		||||
    "account/settings": "사용자 설정",
 | 
			
		||||
    "account/watched": "%1님이 지켜보는 주제",
 | 
			
		||||
    "account/upvoted": "%1 님이 upvote한 게시물",
 | 
			
		||||
    "account/downvoted": "%1 님에 의해 Downvote된 게시물",
 | 
			
		||||
    "account/best": "%1 님 최고의 게시물",
 | 
			
		||||
    "confirm": "확인된 이메일",
 | 
			
		||||
    "account/watched": "%1 님이 관심 가진 게시물",
 | 
			
		||||
    "account/upvoted": "%1 님이 추천한 포스트",
 | 
			
		||||
    "account/downvoted": "%1 님에 비추천한 포스트",
 | 
			
		||||
    "account/best": "%1 님 최고의 포스트",
 | 
			
		||||
    "confirm": "이메일 인증 되었습니다",
 | 
			
		||||
    "maintenance.text": "%1 사이트는 현재 점검 중입니다. 나중에 다시 방문해주세요.",
 | 
			
		||||
    "maintenance.messageIntro": "다음은 관리자가 전하는 메시지입니다.",
 | 
			
		||||
    "throttled.text": "과도한 부하로 %1 를 로드할 수 없습니다. 잠시후에 다시 시도해주세요."
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,19 @@
 | 
			
		||||
{
 | 
			
		||||
    "title": "최근 주제",
 | 
			
		||||
    "title": "최근 게시글",
 | 
			
		||||
    "day": "일간",
 | 
			
		||||
    "week": "주간",
 | 
			
		||||
    "month": "월간",
 | 
			
		||||
    "year": "연간",
 | 
			
		||||
    "alltime": "전체",
 | 
			
		||||
    "no_recent_topics": "최근 생성된 주제가 없습니다.",
 | 
			
		||||
    "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 개의 게시물이 있습니다.",
 | 
			
		||||
    "there-are-new-topics": "새로운 %1 개의 주제가 있습니다.",
 | 
			
		||||
    "there-are-new-topics-and-a-new-post": "새로운 %1 개의 주제와 게시물이 있습니다.",
 | 
			
		||||
    "there-are-new-topics-and-new-posts": "새로운 %1 개의 주제와 %2 개의 게시물이 있습니다.",
 | 
			
		||||
    "there-is-a-new-post": "새로운 게시물이 있습니다.",
 | 
			
		||||
    "there-are-new-posts": "새로운 %1 개의 게시물이 있습니다.",
 | 
			
		||||
    "no_recent_topics": "최근 생성된 게시물이 없습니다.",
 | 
			
		||||
    "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 개의 포스트가 있습니다.",
 | 
			
		||||
    "there-are-new-topics": "새로운 %1 개의 게시물이 있습니다.",
 | 
			
		||||
    "there-are-new-topics-and-a-new-post": "새로운 %1 개의 게시물과 포스트가 있습니다.",
 | 
			
		||||
    "there-are-new-topics-and-new-posts": "새로운 %1 개의 게시물과 %2 개의 포스트가 있습니다.",
 | 
			
		||||
    "there-is-a-new-post": "새로운 포스트가 있습니다.",
 | 
			
		||||
    "there-are-new-posts": "새로운 %1 개의 포스트가 있습니다.",
 | 
			
		||||
    "click-here-to-reload": "이 곳을 클릭하여 새로고침 하세요."
 | 
			
		||||
}
 | 
			
		||||
@@ -2,17 +2,17 @@
 | 
			
		||||
    "register": "회원가입",
 | 
			
		||||
    "cancel_registration": "회원가입 취소",
 | 
			
		||||
    "help.email": "입력하신 이메일 주소는 공개되지 않으며, 설정을 통해 공개하실 수 있습니다.",
 | 
			
		||||
    "help.username_restrictions": "%1자 이상 %2자 이하의 고유한 이름을 입력하세요. @<span id='yourUsername'>username</span> 같은 방식으로 다른 사람들을 언급할 수 있습니다.",
 | 
			
		||||
    "help.username_restrictions": "%1자 이상 %2자 이하의 고유한 사용자명을 입력하세요. @<span id='yourUsername'>username</span> 같은 방식으로 다른 사람들을 언급할 수 있습니다.",
 | 
			
		||||
    "help.minimum_password_length": "비밀번호는 최소 %1자로 제한됩니다.",
 | 
			
		||||
    "email_address": "이메일",
 | 
			
		||||
    "email_address_placeholder": "여기에 이메일 주소를 입력하세요.",
 | 
			
		||||
    "username": "사용자 이름",
 | 
			
		||||
    "username_placeholder": "여기에 사용자 이름을 입력하세요.",
 | 
			
		||||
    "username": "사용자명",
 | 
			
		||||
    "username_placeholder": "여기에 사용자명을 입력하세요.",
 | 
			
		||||
    "password": "비밀번호",
 | 
			
		||||
    "password_placeholder": "여기에 비밀번호를 입력하세요.",
 | 
			
		||||
    "confirm_password": "비밀번호 재입력",
 | 
			
		||||
    "confirm_password_placeholder": "여기에 비밀번호를 재입력하세요.",
 | 
			
		||||
    "register_now_button": "회원가입",
 | 
			
		||||
    "confirm_password": "비밀번호 확인",
 | 
			
		||||
    "confirm_password_placeholder": "여기에 비밀번호 확인을 입력하세요.",
 | 
			
		||||
    "register_now_button": "가입하기",
 | 
			
		||||
    "alternative_registration": "다른 방법으로 회원가입",
 | 
			
		||||
    "terms_of_use": "이용약관",
 | 
			
		||||
    "agree_to_terms_of_use": "이용약관에 동의합니다.",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,17 +1,17 @@
 | 
			
		||||
{
 | 
			
		||||
    "reset_password": "비밀번호 초기화",
 | 
			
		||||
    "update_password": "비밀번호 변경",
 | 
			
		||||
    "password_changed.title": "비밀번호가 변경되었습니다.",
 | 
			
		||||
    "password_changed.message": "<p>성공적으로 비밀번호가 초기화되었습니다. 다시 <a href=\"/login\">로그인</a>해 주세요.",
 | 
			
		||||
    "reset_password": "패스워드 재설정",
 | 
			
		||||
    "update_password": "패스워드 변경",
 | 
			
		||||
    "password_changed.title": "패스워드가 변경되었습니다.",
 | 
			
		||||
    "password_changed.message": "<p>성공적으로 패스워드가 재설정 되었습니다. 다시 <a href=\"/login\">로그인</a>해 주세요.",
 | 
			
		||||
    "wrong_reset_code.title": "올바르지 않은 초기화 코드입니다.",
 | 
			
		||||
    "wrong_reset_code.message": "올바르지 않은 초기화 코드입니다. 다시 시도하거나 <a href=\"/reset\">새로운 초기화 코드를 요청</a>하세요.",
 | 
			
		||||
    "new_password": "새 비밀번호",
 | 
			
		||||
    "repeat_password": "비밀번호 재입력",
 | 
			
		||||
    "enter_email": "<strong>이메일 주소</strong>를 입력하면 비밀번호를 초기화하는 방법을 메일로 알려드립니다.",
 | 
			
		||||
    "enter_email_address": "여기에 이메일 주소를 입력하세요.",
 | 
			
		||||
    "password_reset_sent": "이메일이 발송되었습니다.",
 | 
			
		||||
    "wrong_reset_code.message": "올바르지 않은 재설정 코드입니다. 다시 시도하거나 <a href=\"/reset\">새로운 재설정 코드를 요청</a>하세요.",
 | 
			
		||||
    "new_password": "새 패스워드",
 | 
			
		||||
    "repeat_password": "패스워드 확인",
 | 
			
		||||
    "enter_email": "<strong>이메일 주소</strong>를 입력하면 패스워드를 재설정하는 방법을 메일로 알려드립니다.",
 | 
			
		||||
    "enter_email_address": "이메일 주소를 입력하세요.",
 | 
			
		||||
    "password_reset_sent": "패스워드 재설정 이메일이 발송되었습니다.",
 | 
			
		||||
    "invalid_email": "올바르지 않거나 가입되지 않은 이메일입니다.",
 | 
			
		||||
    "password_too_short": "입력한 비밀번호가 너무 짧습니다, 다시 입력하세요.",
 | 
			
		||||
    "passwords_do_not_match": "비밀번호와 재입력한 비밀번호가 일치하지 않습니다.",
 | 
			
		||||
    "password_expired": "비밀번호가 만료되었습니다, 새로운 비밀번호를 입력하세요."
 | 
			
		||||
    "password_too_short": "입력한 패스워드가 너무 짧습니다, 다시 입력하세요.",
 | 
			
		||||
    "passwords_do_not_match": "패스워드와 패스워드 확인이 일치하지 않습니다.",
 | 
			
		||||
    "password_expired": "패스워드가 만료되었습니다, 새로운 패스워드를 입력하세요."
 | 
			
		||||
}
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
 | 
			
		||||
	"mongo": "Mongo",
 | 
			
		||||
	"mongo.version": "Wersja MongoDB",
 | 
			
		||||
	"mongo.storage-engine": "Storage Engine",
 | 
			
		||||
	"mongo.storage-engine": "Silnik Magazynowania",
 | 
			
		||||
	"mongo.collections": "Kolekcje",
 | 
			
		||||
	"mongo.objects": "Obiekty",
 | 
			
		||||
	"mongo.avg-object-size": "Przybliżony rozmiar obiektu",
 | 
			
		||||
@@ -15,7 +15,7 @@
 | 
			
		||||
	"mongo.storage-size": "Rozmiar pamięci",
 | 
			
		||||
	"mongo.index-size": "Rozmiar indeksu",
 | 
			
		||||
	"mongo.file-size": "Rozmiar pliku",
 | 
			
		||||
	"mongo.resident-memory": "Resident Memory",
 | 
			
		||||
	"mongo.resident-memory": "Przynależna Pamięć",
 | 
			
		||||
	"mongo.virtual-memory": "Pamięc wirtualna",
 | 
			
		||||
	"mongo.mapped-memory": "Pamięc zmapowana",
 | 
			
		||||
	"mongo.raw-info": "Surowe informacje MongoDB",
 | 
			
		||||
@@ -29,8 +29,8 @@
 | 
			
		||||
	"redis.memory-frag-ratio": "Proporcja fragmentacji pamięci",
 | 
			
		||||
	"redis.total-connections-recieved": "Otrzymanych połączeń",
 | 
			
		||||
	"redis.total-commands-processed": "Przetworzonych połączeń",
 | 
			
		||||
	"redis.iops": "Instantaneous Ops. Per Second",
 | 
			
		||||
	"redis.keyspace-hits": "Keyspace Hits",
 | 
			
		||||
	"redis.keyspace-misses": "Keyspace Misses",
 | 
			
		||||
	"redis.iops": "Natychmiastowe Operacje Na Sekundę",
 | 
			
		||||
	"redis.keyspace-hits": "Trafienia Kluczy",
 | 
			
		||||
	"redis.keyspace-misses": "Nietrafione Klucze",
 | 
			
		||||
	"redis.raw-info": "Surowe informacje Redis"
 | 
			
		||||
}
 | 
			
		||||
@@ -7,6 +7,6 @@
 | 
			
		||||
	"custom-header.description": "Wpisz tutaj kod HTML (JavaScript, tagi <code><meta></code>) który ma być dołączony do sekcji <code><head></code> w szablonie forum.",
 | 
			
		||||
	"custom-header.enable": "Włącz własny nagłówek",
 | 
			
		||||
 | 
			
		||||
	"custom-css.livereload": "Enable Live Reload",
 | 
			
		||||
	"custom-css.livereload.description": "Enable this to force all sessions on every device under your account to refresh whenever you click save"
 | 
			
		||||
	"custom-css.livereload": "Włącz żywe przeładowanie",
 | 
			
		||||
	"custom-css.livereload.description": "Włącz to, aby zmusić wszystkie sesje do odświeżenia na każdym urządzeniu na twoim koncie gdziekolwiek klikniesz zapisz."
 | 
			
		||||
}
 | 
			
		||||
@@ -1,12 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
	"you-are-on": "Informacja - Jesteś na  <strong>%1:%2</strong>",
 | 
			
		||||
	"nodes-responded": "%1 nodes responded within %2ms!",
 | 
			
		||||
	"nodes-responded": "%1 nodes odpowiedziały w ciągu %2ms!",
 | 
			
		||||
	"host": "Host",
 | 
			
		||||
	"pid": "pid",
 | 
			
		||||
	"nodejs": "nodejs",
 | 
			
		||||
	"online": "online",
 | 
			
		||||
	"git": "git",
 | 
			
		||||
	"memory": "memory",
 | 
			
		||||
	"memory": "pamięć",
 | 
			
		||||
	"load": "obciążenie",
 | 
			
		||||
	"uptime": "uptime",
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -14,8 +14,8 @@
 | 
			
		||||
	"dev-interested": "Zainteresowany pisanie pluginów do NodeBB??",
 | 
			
		||||
	"docs-info": "Pełna dokumentacje dotycząca pisania pluginów znajduje się tutaj <a target=\"_blank\" href=\"https://docs.nodebb.org/en/latest/plugins/create.html\">NodeBB Docs Portal</a>.",
 | 
			
		||||
 | 
			
		||||
	"order.description": "Certain plugins work ideally when they are initialised before/after other plugins.",
 | 
			
		||||
	"order.explanation": "Plugins load in the order specified here, from top to bottom",
 | 
			
		||||
	"order.description": "Pewne pluginy działają idealnie kiedy są zainicjalizowane przed/po innymi pluginami.",
 | 
			
		||||
	"order.explanation": "Pluginy ładują się tutaj w określonej kolejności, od góry do dołu.",
 | 
			
		||||
	
 | 
			
		||||
	"plugin-item.themes": "Style",
 | 
			
		||||
	"plugin-item.deactivate": "Dezaktywować",
 | 
			
		||||
@@ -28,7 +28,7 @@
 | 
			
		||||
	"plugin-item.upgrade": "Zaktualizuj",
 | 
			
		||||
	"plugin-item.more-info": "Po więcej informacji:",
 | 
			
		||||
	"plugin-item.unknown": "Nieznane",
 | 
			
		||||
	"plugin-item.unknown-explanation": "The state of this plugin could not be determined, possibly due to a misconfiguration error.",
 | 
			
		||||
	"plugin-item.unknown-explanation": "Stan tego pluginu nie może być ustalony, prawdopodobnie z powodu błędu konfiguracji błędów.",
 | 
			
		||||
 | 
			
		||||
	"alert.enabled": "Plugin Włączony",
 | 
			
		||||
	"alert.disabled": "Plugin Wyłączony",
 | 
			
		||||
@@ -40,8 +40,8 @@
 | 
			
		||||
	"alert.upgrade-success": "Proszę przeładować NodeBB, aby poprawnie działał plugin",
 | 
			
		||||
	"alert.install-success": "Plugin pomyślnie zainstalowany, proszę aktywować go.",
 | 
			
		||||
	"alert.uninstall-success": "Plugin został pomyślnie zdezaktywowany oraz odinstalowany.",
 | 
			
		||||
	"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.suggest-error": "<p>NodeBB nie może dostać się do menedżera pakietów, kontynuować instalacji ostatniej wersji?</p><div class=\"alert alert-danger\"><strong>Serwer zwrócił (%1)</strong>: %2</div>",
 | 
			
		||||
	"alert.package-manager-unreachable": "<p>NodeBB nie może dostać się do menedżera pakietów, aktualizacja nie jest sugerowana w tym momencie.</p>",
 | 
			
		||||
	"alert.incompatible": "<p>Twoja wersja NodeBB (v%1) jest usuwana w celu uaktualnienia do v%2 tego pluginu. Proszę zaktualizuj twoje NodeBB jeżeli chcesz zainstalować nowszą wersję tego pluginu.</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>"
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,7 @@
 | 
			
		||||
    "details.disableJoinRequests": "Wyłączono prośbę o dołączenie",
 | 
			
		||||
    "details.grant": "Nadaj/Cofnij prawa Właściciela",
 | 
			
		||||
    "details.kick": "Wykop",
 | 
			
		||||
    "details.kick_confirm": "Are you sure you want to remove this member from the group?",
 | 
			
		||||
    "details.kick_confirm": "Jesteś pewny, że chcesz wyrzucić tego użytkownika z grupy?",
 | 
			
		||||
    "details.owner_options": "Administracja grupy",
 | 
			
		||||
    "details.group_name": "Nazwa grupy",
 | 
			
		||||
    "details.member_count": "Liczba Członków",
 | 
			
		||||
 
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
    "chat.three_months": "3 miesiące",
 | 
			
		||||
    "chat.delete_message_confirm": "Jesteś pewny, że chcesz usunąć tą wiadomość?",
 | 
			
		||||
    "chat.add-users-to-room": "Dodaj użytkownika do pokoju czatu",
 | 
			
		||||
    "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.confirm-chat-with-dnd-user": "Ten użytkownik ustawił swój status na \"nie przeszkadzać\". Czy wciąż chcesz z nim rozmawiać?",
 | 
			
		||||
    "composer.compose": "Twórz",
 | 
			
		||||
    "composer.show_preview": "Pokaż Podgląd",
 | 
			
		||||
    "composer.hide_preview": "Ukryj Podgląd",
 | 
			
		||||
 
 | 
			
		||||
@@ -15,8 +15,8 @@ define('forum/category', [
 | 
			
		||||
], function (infinitescroll, share, navigator, categoryTools, sort, components, translator, topicSelect, pagination, storage) {
 | 
			
		||||
	var Category = {};
 | 
			
		||||
 | 
			
		||||
	$(window).on('action:ajaxify.end', function (ev, data) {
 | 
			
		||||
		if (data.tpl_url !== 'category') {
 | 
			
		||||
	$(window).on('action:ajaxify.start', function (ev, data) {
 | 
			
		||||
		if (data.url && !data.url.startsWith('category/')) {
 | 
			
		||||
			navigator.disable();
 | 
			
		||||
 | 
			
		||||
			removeListeners();
 | 
			
		||||
 
 | 
			
		||||
@@ -23,16 +23,14 @@ define('forum/topic', [
 | 
			
		||||
			Topic.replaceURLTimeout = 0;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (ajaxify.currentPage !== data.url) {
 | 
			
		||||
		if (data.url && !data.url.startsWith('topic/')) {
 | 
			
		||||
			navigator.disable();
 | 
			
		||||
			components.get('navbar/title').find('span').text('').hide();
 | 
			
		||||
			app.removeAlert('bookmark');
 | 
			
		||||
 | 
			
		||||
			events.removeListeners();
 | 
			
		||||
			$(window).off('keydown', onKeyDown);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (data.url && !data.url.startsWith('topic/')) {
 | 
			
		||||
			require(['search'], function (search) {
 | 
			
		||||
				if (search.topicDOM.active) {
 | 
			
		||||
					search.topicDOM.end();
 | 
			
		||||
 
 | 
			
		||||
@@ -199,7 +199,7 @@ define('forum/topic/postTools', [
 | 
			
		||||
		var selectedNode = getSelectedNode();
 | 
			
		||||
 | 
			
		||||
		showStaleWarning(function () {
 | 
			
		||||
			var username = getUserName(button);
 | 
			
		||||
			var username = getUserSlug(button);
 | 
			
		||||
			if (getData(button, 'data-uid') === '0' || !getData(button, 'data-userslug')) {
 | 
			
		||||
				username = '';
 | 
			
		||||
			}
 | 
			
		||||
@@ -231,7 +231,7 @@ define('forum/topic/postTools', [
 | 
			
		||||
		var selectedNode = getSelectedNode();
 | 
			
		||||
 | 
			
		||||
		showStaleWarning(function () {
 | 
			
		||||
			var username = getUserName(button);
 | 
			
		||||
			var username = getUserSlug(button);
 | 
			
		||||
			var toPid = getData(button, 'data-pid');
 | 
			
		||||
 | 
			
		||||
			function quote(text) {
 | 
			
		||||
@@ -284,7 +284,7 @@ define('forum/topic/postTools', [
 | 
			
		||||
			selectedText = range.toString();
 | 
			
		||||
			var postEl = $(content).parents('[component="post"]');
 | 
			
		||||
			selectedPid = postEl.attr('data-pid');
 | 
			
		||||
			username = getUserName($(content));
 | 
			
		||||
			username = getUserSlug($(content));
 | 
			
		||||
			range.detach();
 | 
			
		||||
		}
 | 
			
		||||
		return { text: selectedText, pid: selectedPid, username: username };
 | 
			
		||||
@@ -309,22 +309,22 @@ define('forum/topic/postTools', [
 | 
			
		||||
		return button.parents('[data-pid]').attr(data);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function getUserName(button) {
 | 
			
		||||
		var username = '';
 | 
			
		||||
	function getUserSlug(button) {
 | 
			
		||||
		var slug = '';
 | 
			
		||||
		var post = button.parents('[data-pid]');
 | 
			
		||||
 | 
			
		||||
		if (button.attr('component') === 'topic/reply') {
 | 
			
		||||
			return username;
 | 
			
		||||
			return slug;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (post.length) {
 | 
			
		||||
			username = post.attr('data-username').replace(/\s/g, '-');
 | 
			
		||||
			slug = post.attr('data-userslug');
 | 
			
		||||
		}
 | 
			
		||||
		if (post.length && post.attr('data-uid') !== '0') {
 | 
			
		||||
			username = '@' + username;
 | 
			
		||||
			slug = '@' + slug;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return username;
 | 
			
		||||
		return slug;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	function togglePostDelete(button, tid) {
 | 
			
		||||
 
 | 
			
		||||
@@ -470,6 +470,9 @@ define('settings', function () {
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				// Save loaded settings into ajaxify.data for use client-side
 | 
			
		||||
				ajaxify.data.settings = values;
 | 
			
		||||
 | 
			
		||||
				$(formEl).deserialize(values);
 | 
			
		||||
				$(formEl).find('input[type="checkbox"]').each(function () {
 | 
			
		||||
					$(this).parents('.mdl-switch').toggleClass('is-checked', $(this).is(':checked'));
 | 
			
		||||
@@ -510,6 +513,9 @@ define('settings', function () {
 | 
			
		||||
					// Remove unsaved flag to re-enable ajaxify
 | 
			
		||||
					app.flags._unsaved = false;
 | 
			
		||||
 | 
			
		||||
					// Also save to local ajaxify.data
 | 
			
		||||
					ajaxify.data.settings = values;
 | 
			
		||||
 | 
			
		||||
					if (typeof callback === 'function') {
 | 
			
		||||
						callback(err);
 | 
			
		||||
					} else if (err) {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ categoryController.get = function (req, res, callback) {
 | 
			
		||||
	var pageCount = 1;
 | 
			
		||||
	var userPrivileges;
 | 
			
		||||
	var settings;
 | 
			
		||||
	var rssToken;
 | 
			
		||||
 | 
			
		||||
	if ((req.params.topic_index && !utils.isNumber(req.params.topic_index)) || !utils.isNumber(cid)) {
 | 
			
		||||
		return callback();
 | 
			
		||||
@@ -41,10 +42,14 @@ categoryController.get = function (req, res, callback) {
 | 
			
		||||
				userSettings: function (next) {
 | 
			
		||||
					user.getSettings(req.uid, next);
 | 
			
		||||
				},
 | 
			
		||||
				rssToken: function (next) {
 | 
			
		||||
					user.auth.getFeedToken(req.uid, next);
 | 
			
		||||
				},
 | 
			
		||||
			}, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (results, next) {
 | 
			
		||||
			userPrivileges = results.privileges;
 | 
			
		||||
			rssToken = results.rssToken;
 | 
			
		||||
 | 
			
		||||
			if (!results.categoryData.slug || (results.categoryData && parseInt(results.categoryData.disabled, 10) === 1)) {
 | 
			
		||||
				return callback();
 | 
			
		||||
@@ -121,15 +126,15 @@ categoryController.get = function (req, res, callback) {
 | 
			
		||||
			categoryData.description = translator.escape(categoryData.description);
 | 
			
		||||
			categoryData.privileges = userPrivileges;
 | 
			
		||||
			categoryData.showSelect = categoryData.privileges.editable;
 | 
			
		||||
			categoryData.rssFeedUrl = nconf.get('url') + '/category/' + categoryData.cid + '.rss';
 | 
			
		||||
			if (parseInt(req.uid, 10)) {
 | 
			
		||||
				categories.markAsRead([cid], req.uid);
 | 
			
		||||
				categoryData.rssFeedUrl += '?uid=' + req.uid + '&token=' + rssToken;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			addTags(categoryData, res);
 | 
			
		||||
 | 
			
		||||
			if (parseInt(req.uid, 10)) {
 | 
			
		||||
				categories.markAsRead([cid], req.uid);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			categoryData['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1;
 | 
			
		||||
			categoryData.rssFeedUrl = nconf.get('relative_path') + '/category/' + categoryData.cid + '.rss';
 | 
			
		||||
			categoryData.title = translator.escape(categoryData.name);
 | 
			
		||||
			pageCount = Math.max(1, Math.ceil(categoryData.topic_count / settings.topicsPerPage));
 | 
			
		||||
			categoryData.pagination = pagination.create(currentPage, pageCount, req.query);
 | 
			
		||||
@@ -192,7 +197,7 @@ function addTags(categoryData, res) {
 | 
			
		||||
		{
 | 
			
		||||
			rel: 'alternate',
 | 
			
		||||
			type: 'application/rss+xml',
 | 
			
		||||
			href: nconf.get('url') + '/category/' + categoryData.cid + '.rss',
 | 
			
		||||
			href: categoryData.rssFeedUrl,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			rel: 'up',
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,7 @@ var helpers = require('./helpers');
 | 
			
		||||
var pagination = require('../pagination');
 | 
			
		||||
var utils = require('../utils');
 | 
			
		||||
 | 
			
		||||
var topicsController = {};
 | 
			
		||||
var topicsController = module.exports;
 | 
			
		||||
 | 
			
		||||
topicsController.get = function (req, res, callback) {
 | 
			
		||||
	var tid = req.params.topic_id;
 | 
			
		||||
@@ -23,6 +23,7 @@ topicsController.get = function (req, res, callback) {
 | 
			
		||||
	var pageCount = 1;
 | 
			
		||||
	var userPrivileges;
 | 
			
		||||
	var settings;
 | 
			
		||||
	var rssToken;
 | 
			
		||||
 | 
			
		||||
	if ((req.params.post_index && !utils.isNumber(req.params.post_index)) || !utils.isNumber(tid)) {
 | 
			
		||||
		return callback();
 | 
			
		||||
@@ -40,6 +41,9 @@ topicsController.get = function (req, res, callback) {
 | 
			
		||||
				topic: function (next) {
 | 
			
		||||
					topics.getTopicData(tid, next);
 | 
			
		||||
				},
 | 
			
		||||
				rssToken: function (next) {
 | 
			
		||||
					user.auth.getFeedToken(req.uid, next);
 | 
			
		||||
				},
 | 
			
		||||
			}, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (results, next) {
 | 
			
		||||
@@ -48,6 +52,7 @@ topicsController.get = function (req, res, callback) {
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			userPrivileges = results.privileges;
 | 
			
		||||
			rssToken = results.rssToken;
 | 
			
		||||
 | 
			
		||||
			if (!userPrivileges['topics:read'] || (parseInt(results.topic.deleted, 10) && !userPrivileges.view_deleted)) {
 | 
			
		||||
				return helpers.notAllowed(req, res);
 | 
			
		||||
@@ -129,25 +134,77 @@ topicsController.get = function (req, res, callback) {
 | 
			
		||||
			plugins.fireHook('filter:controllers.topic.get', { topicData: topicData, uid: req.uid }, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (data, next) {
 | 
			
		||||
			buildBreadcrumbs(data.topicData, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (topicData) {
 | 
			
		||||
			topicData.privileges = userPrivileges;
 | 
			
		||||
			topicData.topicStaleDays = parseInt(meta.config.topicStaleDays, 10) || 60;
 | 
			
		||||
			topicData['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
 | 
			
		||||
			topicData['downvote:disabled'] = parseInt(meta.config['downvote:disabled'], 10) === 1;
 | 
			
		||||
			topicData['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1;
 | 
			
		||||
			topicData.bookmarkThreshold = parseInt(meta.config.bookmarkThreshold, 10) || 5;
 | 
			
		||||
			topicData.postEditDuration = parseInt(meta.config.postEditDuration, 10) || 0;
 | 
			
		||||
			topicData.postDeleteDuration = parseInt(meta.config.postDeleteDuration, 10) || 0;
 | 
			
		||||
			topicData.scrollToMyPost = settings.scrollToMyPost;
 | 
			
		||||
			topicData.rssFeedUrl = nconf.get('relative_path') + '/topic/' + topicData.tid + '.rss';
 | 
			
		||||
			if (req.uid) {
 | 
			
		||||
				topicData.rssFeedUrl += '?uid=' + req.uid + '&token=' + rssToken;
 | 
			
		||||
			}
 | 
			
		||||
			topicData.postIndex = req.params.post_index;
 | 
			
		||||
			topicData.pagination = pagination.create(currentPage, pageCount, req.query);
 | 
			
		||||
			topicData.pagination.rel.forEach(function (rel) {
 | 
			
		||||
				rel.href = nconf.get('url') + '/topic/' + topicData.slug + rel.href;
 | 
			
		||||
				res.locals.linkTags.push(rel);
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			req.session.tids_viewed = req.session.tids_viewed || {};
 | 
			
		||||
			if (!req.session.tids_viewed[tid] || req.session.tids_viewed[tid] < Date.now() - 3600000) {
 | 
			
		||||
				topics.increaseViewCount(tid);
 | 
			
		||||
				req.session.tids_viewed[tid] = Date.now();
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			addTags(topicData, req, res);
 | 
			
		||||
 | 
			
		||||
			if (req.uid) {
 | 
			
		||||
				topics.markAsRead([tid], req.uid, function (err, markedRead) {
 | 
			
		||||
					if (err) {
 | 
			
		||||
						return callback(err);
 | 
			
		||||
					}
 | 
			
		||||
					if (markedRead) {
 | 
			
		||||
						topics.pushUnreadCount(req.uid);
 | 
			
		||||
						topics.markTopicNotificationsRead([tid], req.uid);
 | 
			
		||||
					}
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			res.render('topic', topicData);
 | 
			
		||||
		},
 | 
			
		||||
	], callback);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function buildBreadcrumbs(topicData, callback) {
 | 
			
		||||
	var breadcrumbs = [
 | 
			
		||||
		{
 | 
			
		||||
					text: data.topicData.category.name,
 | 
			
		||||
					url: nconf.get('relative_path') + '/category/' + data.topicData.category.slug,
 | 
			
		||||
			text: topicData.category.name,
 | 
			
		||||
			url: nconf.get('relative_path') + '/category/' + topicData.category.slug,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
					text: data.topicData.title,
 | 
			
		||||
			text: topicData.title,
 | 
			
		||||
		},
 | 
			
		||||
	];
 | 
			
		||||
 | 
			
		||||
			helpers.buildCategoryBreadcrumbs(data.topicData.category.parentCid, function (err, crumbs) {
 | 
			
		||||
				if (err) {
 | 
			
		||||
					return next(err);
 | 
			
		||||
				}
 | 
			
		||||
				data.topicData.breadcrumbs = crumbs.concat(breadcrumbs);
 | 
			
		||||
				next(null, data.topicData);
 | 
			
		||||
			});
 | 
			
		||||
	async.waterfall([
 | 
			
		||||
		function (next) {
 | 
			
		||||
			helpers.buildCategoryBreadcrumbs(topicData.category.parentCid, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (topicData, next) {
 | 
			
		||||
		function (crumbs, next) {
 | 
			
		||||
			topicData.breadcrumbs = crumbs.concat(breadcrumbs);
 | 
			
		||||
			next(null, topicData);
 | 
			
		||||
		},
 | 
			
		||||
	], callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function addTags(topicData, req, res) {
 | 
			
		||||
	function findPost(index) {
 | 
			
		||||
		for (var i = 0; i < topicData.posts.length; i += 1) {
 | 
			
		||||
			if (parseInt(topicData.posts[i].index, 10) === parseInt(index, 10)) {
 | 
			
		||||
@@ -184,7 +241,6 @@ topicsController.get = function (req, res, callback) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	description = description.replace(/\n/g, ' ');
 | 
			
		||||
 | 
			
		||||
	res.locals.metaTags = [
 | 
			
		||||
		{
 | 
			
		||||
			name: 'title',
 | 
			
		||||
@@ -234,7 +290,7 @@ topicsController.get = function (req, res, callback) {
 | 
			
		||||
		{
 | 
			
		||||
			rel: 'alternate',
 | 
			
		||||
			type: 'application/rss+xml',
 | 
			
		||||
					href: nconf.get('url') + '/topic/' + tid + '.rss',
 | 
			
		||||
			href: topicData.rssFeedUrl,
 | 
			
		||||
		},
 | 
			
		||||
	];
 | 
			
		||||
 | 
			
		||||
@@ -244,52 +300,7 @@ topicsController.get = function (req, res, callback) {
 | 
			
		||||
			href: nconf.get('url') + '/category/' + topicData.category.slug,
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
			next(null, topicData);
 | 
			
		||||
		},
 | 
			
		||||
	], function (err, data) {
 | 
			
		||||
		if (err) {
 | 
			
		||||
			return callback(err);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		data.privileges = userPrivileges;
 | 
			
		||||
		data.topicStaleDays = parseInt(meta.config.topicStaleDays, 10) || 60;
 | 
			
		||||
		data['reputation:disabled'] = parseInt(meta.config['reputation:disabled'], 10) === 1;
 | 
			
		||||
		data['downvote:disabled'] = parseInt(meta.config['downvote:disabled'], 10) === 1;
 | 
			
		||||
		data['feeds:disableRSS'] = parseInt(meta.config['feeds:disableRSS'], 10) === 1;
 | 
			
		||||
		data.bookmarkThreshold = parseInt(meta.config.bookmarkThreshold, 10) || 5;
 | 
			
		||||
		data.postEditDuration = parseInt(meta.config.postEditDuration, 10) || 0;
 | 
			
		||||
		data.postDeleteDuration = parseInt(meta.config.postDeleteDuration, 10) || 0;
 | 
			
		||||
		data.scrollToMyPost = settings.scrollToMyPost;
 | 
			
		||||
		data.rssFeedUrl = nconf.get('relative_path') + '/topic/' + data.tid + '.rss';
 | 
			
		||||
		data.postIndex = req.params.post_index;
 | 
			
		||||
		data.pagination = pagination.create(currentPage, pageCount, req.query);
 | 
			
		||||
		data.pagination.rel.forEach(function (rel) {
 | 
			
		||||
			rel.href = nconf.get('url') + '/topic/' + data.slug + rel.href;
 | 
			
		||||
			res.locals.linkTags.push(rel);
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		req.session.tids_viewed = req.session.tids_viewed || {};
 | 
			
		||||
		if (!req.session.tids_viewed[tid] || req.session.tids_viewed[tid] < Date.now() - 3600000) {
 | 
			
		||||
			topics.increaseViewCount(tid);
 | 
			
		||||
			req.session.tids_viewed[tid] = Date.now();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (req.uid) {
 | 
			
		||||
			topics.markAsRead([tid], req.uid, function (err, markedRead) {
 | 
			
		||||
				if (err) {
 | 
			
		||||
					return callback(err);
 | 
			
		||||
				}
 | 
			
		||||
				if (markedRead) {
 | 
			
		||||
					topics.pushUnreadCount(req.uid);
 | 
			
		||||
					topics.markTopicNotificationsRead([tid], req.uid);
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		res.render('topic', data);
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
topicsController.teaser = function (req, res, next) {
 | 
			
		||||
	var tid = req.params.topic_id;
 | 
			
		||||
@@ -355,5 +366,3 @@ topicsController.pagination = function (req, res, callback) {
 | 
			
		||||
		res.json(paginationData);
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
module.exports = topicsController;
 | 
			
		||||
 
 | 
			
		||||
@@ -206,18 +206,18 @@ mongoModule.info = function (db, callback) {
 | 
			
		||||
 | 
			
		||||
			stats.mem = results.serverStatus.mem;
 | 
			
		||||
			stats.mem = results.serverStatus.mem;
 | 
			
		||||
			stats.mem.resident = (stats.mem.resident / 1024).toFixed(2);
 | 
			
		||||
			stats.mem.virtual = (stats.mem.virtual / 1024).toFixed(2);
 | 
			
		||||
			stats.mem.mapped = (stats.mem.mapped / 1024).toFixed(2);
 | 
			
		||||
			stats.mem.resident = (stats.mem.resident / 1024).toFixed(3);
 | 
			
		||||
			stats.mem.virtual = (stats.mem.virtual / 1024).toFixed(3);
 | 
			
		||||
			stats.mem.mapped = (stats.mem.mapped / 1024).toFixed(3);
 | 
			
		||||
			stats.collectionData = results.listCollections;
 | 
			
		||||
			stats.network = results.serverStatus.network;
 | 
			
		||||
			stats.raw = JSON.stringify(stats, null, 4);
 | 
			
		||||
 | 
			
		||||
			stats.avgObjSize = stats.avgObjSize.toFixed(2);
 | 
			
		||||
			stats.dataSize = (stats.dataSize / scale).toFixed(2);
 | 
			
		||||
			stats.storageSize = (stats.storageSize / scale).toFixed(2);
 | 
			
		||||
			stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(2) : 0;
 | 
			
		||||
			stats.indexSize = (stats.indexSize / scale).toFixed(2);
 | 
			
		||||
			stats.dataSize = (stats.dataSize / scale).toFixed(3);
 | 
			
		||||
			stats.storageSize = (stats.storageSize / scale).toFixed(3);
 | 
			
		||||
			stats.fileSize = stats.fileSize ? (stats.fileSize / scale).toFixed(3) : 0;
 | 
			
		||||
			stats.indexSize = (stats.indexSize / scale).toFixed(3);
 | 
			
		||||
			stats.storageEngine = results.serverStatus.storageEngine ? results.serverStatus.storageEngine.name : 'mmapv1';
 | 
			
		||||
			stats.host = results.serverStatus.host;
 | 
			
		||||
			stats.version = results.serverStatus.version;
 | 
			
		||||
 
 | 
			
		||||
@@ -152,7 +152,7 @@ redisModule.info = function (cxn, callback) {
 | 
			
		||||
					redisData[parts[0]] = parts[1];
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
			redisData.used_memory_human = (redisData.used_memory / (1024 * 1024 * 1024)).toFixed(2);
 | 
			
		||||
			redisData.used_memory_human = (redisData.used_memory / (1024 * 1024 * 1024)).toFixed(3);
 | 
			
		||||
			redisData.raw = JSON.stringify(redisData, null, 4);
 | 
			
		||||
			redisData.redis = true;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -36,7 +36,9 @@ Settings.set = function (hash, values, callback) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Settings.setOne = function (hash, field, value, callback) {
 | 
			
		||||
	db.setObjectField('settings:' + hash, field, value, callback);
 | 
			
		||||
	var data = {};
 | 
			
		||||
	data[field] = value;
 | 
			
		||||
	Settings.set(hash, data, callback);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Settings.setOnEmpty = function (hash, values, callback) {
 | 
			
		||||
@@ -54,7 +56,7 @@ Settings.setOnEmpty = function (hash, values, callback) {
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			if (Object.keys(empty).length) {
 | 
			
		||||
				db.setObject('settings:' + hash, empty, next);
 | 
			
		||||
				Settings.set(hash, empty, next);
 | 
			
		||||
			} else {
 | 
			
		||||
				next();
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -26,6 +26,45 @@ module.exports = function (app, middleware) {
 | 
			
		||||
	app.get('/tags/:tag.rss', middleware.maintenanceMode, generateForTag);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function validateTokenIfRequiresLogin(requiresLogin, cid, req, res, callback) {
 | 
			
		||||
	var uid = req.query.uid;
 | 
			
		||||
	var token = req.query.token;
 | 
			
		||||
 | 
			
		||||
	if (!requiresLogin) {
 | 
			
		||||
		return callback();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!uid || !token) {
 | 
			
		||||
		return helpers.notAllowed(req, res);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	async.waterfall([
 | 
			
		||||
		function (next) {
 | 
			
		||||
			user.getUserField(uid, 'rss_token', next);
 | 
			
		||||
		},
 | 
			
		||||
		function (_token, next) {
 | 
			
		||||
			if (token === _token) {
 | 
			
		||||
				async.waterfall([
 | 
			
		||||
					function (next) {
 | 
			
		||||
						privileges.categories.get(cid, uid, next);
 | 
			
		||||
					},
 | 
			
		||||
					function (privileges, next) {
 | 
			
		||||
						if (!privileges.read) {
 | 
			
		||||
							return helpers.notAllowed(req, res);
 | 
			
		||||
						}
 | 
			
		||||
						next();
 | 
			
		||||
					},
 | 
			
		||||
				], callback);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			user.auth.logAttempt(uid, req.ip, next);
 | 
			
		||||
		},
 | 
			
		||||
		function () {
 | 
			
		||||
			helpers.notAllowed(req, res);
 | 
			
		||||
		},
 | 
			
		||||
	], callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateForTopic(req, res, callback) {
 | 
			
		||||
	if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
 | 
			
		||||
		return controllers404.send404(req, res);
 | 
			
		||||
@@ -33,6 +72,7 @@ function generateForTopic(req, res, callback) {
 | 
			
		||||
 | 
			
		||||
	var tid = req.params.topic_id;
 | 
			
		||||
	var userPrivileges;
 | 
			
		||||
	var topic;
 | 
			
		||||
	async.waterfall([
 | 
			
		||||
		function (next) {
 | 
			
		||||
			async.parallel({
 | 
			
		||||
@@ -48,11 +88,12 @@ function generateForTopic(req, res, callback) {
 | 
			
		||||
			if (!results.topic || (parseInt(results.topic.deleted, 10) && !results.privileges.view_deleted)) {
 | 
			
		||||
				return controllers404.send404(req, res);
 | 
			
		||||
			}
 | 
			
		||||
			if (!results.privileges['topics:read']) {
 | 
			
		||||
				return helpers.notAllowed(req, res);
 | 
			
		||||
			}
 | 
			
		||||
			userPrivileges = results.privileges;
 | 
			
		||||
			topics.getTopicWithPosts(results.topic, 'tid:' + tid + ':posts', req.uid, 0, 25, false, next);
 | 
			
		||||
			topic = results.topic;
 | 
			
		||||
			validateTokenIfRequiresLogin(!results.privileges['topics:read'], results.topic.cid, req, res, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (next) {
 | 
			
		||||
			topics.getTopicWithPosts(topic, 'tid:' + tid + ':posts', req.uid || req.query.uid || 0, 0, 25, false, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (topicData) {
 | 
			
		||||
			topics.modifyPostsByPrivilege(topicData, userPrivileges);
 | 
			
		||||
@@ -95,40 +136,12 @@ function generateForTopic(req, res, callback) {
 | 
			
		||||
	], callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateForUserTopics(req, res, callback) {
 | 
			
		||||
	if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
 | 
			
		||||
		return controllers404.send404(req, res);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var userslug = req.params.userslug;
 | 
			
		||||
 | 
			
		||||
	async.waterfall([
 | 
			
		||||
		function (next) {
 | 
			
		||||
			user.getUidByUserslug(userslug, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (uid, next) {
 | 
			
		||||
			if (!uid) {
 | 
			
		||||
				return callback();
 | 
			
		||||
			}
 | 
			
		||||
			user.getUserFields(uid, ['uid', 'username'], next);
 | 
			
		||||
		},
 | 
			
		||||
		function (userData, next) {
 | 
			
		||||
			generateForTopics({
 | 
			
		||||
				uid: req.uid,
 | 
			
		||||
				title: 'Topics by ' + userData.username,
 | 
			
		||||
				description: 'A list of topics that are posted by ' + userData.username,
 | 
			
		||||
				feed_url: '/user/' + userslug + '/topics.rss',
 | 
			
		||||
				site_url: '/user/' + userslug + '/topics',
 | 
			
		||||
			}, 'uid:' + userData.uid + ':topics', req, res, next);
 | 
			
		||||
		},
 | 
			
		||||
	], callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateForCategory(req, res, next) {
 | 
			
		||||
	if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
 | 
			
		||||
		return controllers404.send404(req, res);
 | 
			
		||||
	}
 | 
			
		||||
	var cid = req.params.category_id;
 | 
			
		||||
	var category;
 | 
			
		||||
 | 
			
		||||
	async.waterfall([
 | 
			
		||||
		function (next) {
 | 
			
		||||
@@ -143,22 +156,23 @@ function generateForCategory(req, res, next) {
 | 
			
		||||
						reverse: true,
 | 
			
		||||
						start: 0,
 | 
			
		||||
						stop: 25,
 | 
			
		||||
						uid: req.uid,
 | 
			
		||||
						uid: req.uid || req.query.uid || 0,
 | 
			
		||||
					}, next);
 | 
			
		||||
				},
 | 
			
		||||
			}, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (results, next) {
 | 
			
		||||
			if (!results.privileges.read) {
 | 
			
		||||
				return helpers.notAllowed(req, res);
 | 
			
		||||
			}
 | 
			
		||||
			category = results.category;
 | 
			
		||||
			validateTokenIfRequiresLogin(!results.privileges.read, cid, req, res, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (next) {
 | 
			
		||||
			generateTopicsFeed({
 | 
			
		||||
				uid: req.uid,
 | 
			
		||||
				title: results.category.name,
 | 
			
		||||
				description: results.category.description,
 | 
			
		||||
				uid: req.uid || req.query.uid || 0,
 | 
			
		||||
				title: category.name,
 | 
			
		||||
				description: category.description,
 | 
			
		||||
				feed_url: '/category/' + cid + '.rss',
 | 
			
		||||
				site_url: '/category/' + results.category.cid,
 | 
			
		||||
			}, results.category.topics, next);
 | 
			
		||||
				site_url: '/category/' + category.cid,
 | 
			
		||||
			}, category.topics, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (feed) {
 | 
			
		||||
			sendFeed(feed, res);
 | 
			
		||||
@@ -292,12 +306,13 @@ function generateForRecentPosts(req, res, next) {
 | 
			
		||||
	], next);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateForCategoryRecentPosts(req, res, next) {
 | 
			
		||||
function generateForCategoryRecentPosts(req, res, callback) {
 | 
			
		||||
	if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
 | 
			
		||||
		return controllers404.send404(req, res);
 | 
			
		||||
	}
 | 
			
		||||
	var cid = req.params.category_id;
 | 
			
		||||
 | 
			
		||||
	var category;
 | 
			
		||||
	var posts;
 | 
			
		||||
	async.waterfall([
 | 
			
		||||
		function (next) {
 | 
			
		||||
			async.parallel({
 | 
			
		||||
@@ -308,29 +323,29 @@ function generateForCategoryRecentPosts(req, res, next) {
 | 
			
		||||
					categories.getCategoryData(cid, next);
 | 
			
		||||
				},
 | 
			
		||||
				posts: function (next) {
 | 
			
		||||
					categories.getRecentReplies(cid, req.uid, 20, next);
 | 
			
		||||
					categories.getRecentReplies(cid, req.uid || req.query.uid || 0, 20, next);
 | 
			
		||||
				},
 | 
			
		||||
			}, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (results, next) {
 | 
			
		||||
			if (!results.category) {
 | 
			
		||||
				return next();
 | 
			
		||||
				return controllers404.send404(req, res);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (!results.privileges.read) {
 | 
			
		||||
				return helpers.notAllowed(req, res);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			category = results.category;
 | 
			
		||||
			posts = results.posts;
 | 
			
		||||
			validateTokenIfRequiresLogin(!results.privileges.read, cid, req, res, next);
 | 
			
		||||
		},
 | 
			
		||||
		function () {
 | 
			
		||||
			var feed = generateForPostsFeed({
 | 
			
		||||
				title: results.category.name + ' Recent Posts',
 | 
			
		||||
				description: 'A list of recent posts from ' + results.category.name,
 | 
			
		||||
				title: category.name + ' Recent Posts',
 | 
			
		||||
				description: 'A list of recent posts from ' + category.name,
 | 
			
		||||
				feed_url: '/category/' + cid + '/recentposts.rss',
 | 
			
		||||
				site_url: '/category/' + cid + '/recentposts',
 | 
			
		||||
			}, results.posts);
 | 
			
		||||
			}, posts);
 | 
			
		||||
 | 
			
		||||
			sendFeed(feed, res);
 | 
			
		||||
		},
 | 
			
		||||
	], next);
 | 
			
		||||
	], callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateForPostsFeed(feedOptions, posts) {
 | 
			
		||||
@@ -357,6 +372,35 @@ function generateForPostsFeed(feedOptions, posts) {
 | 
			
		||||
	return feed;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateForUserTopics(req, res, callback) {
 | 
			
		||||
	if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
 | 
			
		||||
		return controllers404.send404(req, res);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var userslug = req.params.userslug;
 | 
			
		||||
 | 
			
		||||
	async.waterfall([
 | 
			
		||||
		function (next) {
 | 
			
		||||
			user.getUidByUserslug(userslug, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (uid, next) {
 | 
			
		||||
			if (!uid) {
 | 
			
		||||
				return callback();
 | 
			
		||||
			}
 | 
			
		||||
			user.getUserFields(uid, ['uid', 'username'], next);
 | 
			
		||||
		},
 | 
			
		||||
		function (userData, next) {
 | 
			
		||||
			generateForTopics({
 | 
			
		||||
				uid: req.uid,
 | 
			
		||||
				title: 'Topics by ' + userData.username,
 | 
			
		||||
				description: 'A list of topics that are posted by ' + userData.username,
 | 
			
		||||
				feed_url: '/user/' + userslug + '/topics.rss',
 | 
			
		||||
				site_url: '/user/' + userslug + '/topics',
 | 
			
		||||
			}, 'uid:' + userData.uid + ':topics', req, res, next);
 | 
			
		||||
		},
 | 
			
		||||
	], callback);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function generateForTag(req, res, next) {
 | 
			
		||||
	if (parseInt(meta.config['feeds:disableRSS'], 10) === 1) {
 | 
			
		||||
		return controllers404.send404(req, res);
 | 
			
		||||
@@ -381,4 +425,3 @@ function sendFeed(feed, res) {
 | 
			
		||||
	var xml = feed.xml();
 | 
			
		||||
	res.type('xml').set('Content-Length', Buffer.byteLength(xml)).send(xml);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -187,8 +187,10 @@ SocketAdmin.config.setMultiple = function (socket, data, callback) {
 | 
			
		||||
					logger.monitorConfig({ io: index.server }, setting);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			plugins.fireHook('action:config.set', { settings: data });
 | 
			
		||||
			setImmediate(next);
 | 
			
		||||
			data.type = 'config-change';
 | 
			
		||||
			data.uid = socket.uid;
 | 
			
		||||
			data.ip = socket.ip;
 | 
			
		||||
			events.log(data, next);
 | 
			
		||||
		},
 | 
			
		||||
	], callback);
 | 
			
		||||
};
 | 
			
		||||
@@ -202,7 +204,19 @@ SocketAdmin.settings.get = function (socket, data, callback) {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
SocketAdmin.settings.set = function (socket, data, callback) {
 | 
			
		||||
	meta.settings.set(data.hash, data.values, callback);
 | 
			
		||||
	async.waterfall([
 | 
			
		||||
		function (next) {
 | 
			
		||||
			meta.settings.set(data.hash, data.values, next);
 | 
			
		||||
		},
 | 
			
		||||
		function (next) {
 | 
			
		||||
			var eventData = data.values;
 | 
			
		||||
			eventData.type = 'settings-change';
 | 
			
		||||
			eventData.uid = socket.uid;
 | 
			
		||||
			eventData.ip = socket.ip;
 | 
			
		||||
			eventData.hash = data.hash;
 | 
			
		||||
			events.log(eventData, next);
 | 
			
		||||
		},
 | 
			
		||||
	], callback);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
SocketAdmin.settings.clearSitemapCache = function (socket, data, callback) {
 | 
			
		||||
 
 | 
			
		||||
@@ -98,7 +98,7 @@ function setupConfigs() {
 | 
			
		||||
	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') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
 | 
			
		||||
	nconf.set('port', urlObject.port || nconf.get('port') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
 | 
			
		||||
	nconf.set('upload_url', '/assets/uploads');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,7 @@ var db = require('../database');
 | 
			
		||||
var meta = require('../meta');
 | 
			
		||||
var events = require('../events');
 | 
			
		||||
var batch = require('../batch');
 | 
			
		||||
var utils = require('../utils');
 | 
			
		||||
 | 
			
		||||
module.exports = function (User) {
 | 
			
		||||
	User.auth = {};
 | 
			
		||||
@@ -47,6 +48,29 @@ module.exports = function (User) {
 | 
			
		||||
		], callback);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	User.auth.getFeedToken = function (uid, callback) {
 | 
			
		||||
		if (!uid) {
 | 
			
		||||
			return callback();
 | 
			
		||||
		}
 | 
			
		||||
		var token;
 | 
			
		||||
		async.waterfall([
 | 
			
		||||
			function (next) {
 | 
			
		||||
				User.getUserField(uid, 'rss_token', next);
 | 
			
		||||
			},
 | 
			
		||||
			function (_token, next) {
 | 
			
		||||
				token = _token || utils.generateUUID();
 | 
			
		||||
				if (!_token) {
 | 
			
		||||
					User.setUserField(uid, 'rss_token', token, next);
 | 
			
		||||
				} else {
 | 
			
		||||
					next();
 | 
			
		||||
				}
 | 
			
		||||
			},
 | 
			
		||||
			function (next) {
 | 
			
		||||
				next(null, token);
 | 
			
		||||
			},
 | 
			
		||||
		], callback);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	User.auth.clearLoginAttempts = function (uid) {
 | 
			
		||||
		db.delete('loginAttempts:' + uid);
 | 
			
		||||
	};
 | 
			
		||||
 
 | 
			
		||||
@@ -283,7 +283,10 @@ module.exports = function (User) {
 | 
			
		||||
			},
 | 
			
		||||
			function (hashedPassword, next) {
 | 
			
		||||
				async.parallel([
 | 
			
		||||
					async.apply(User.setUserField, data.uid, 'password', hashedPassword),
 | 
			
		||||
					async.apply(User.setUserFields, data.uid, {
 | 
			
		||||
						password: hashedPassword,
 | 
			
		||||
						rss_token: utils.generateUUID(),
 | 
			
		||||
					}),
 | 
			
		||||
					async.apply(User.reset.updateExpiry, data.uid),
 | 
			
		||||
					async.apply(User.auth.revokeAllSessions, data.uid),
 | 
			
		||||
				], function (err) {
 | 
			
		||||
 
 | 
			
		||||
@@ -133,8 +133,8 @@ function setupExpressApp(app, callback) {
 | 
			
		||||
 | 
			
		||||
	app.use(compression());
 | 
			
		||||
 | 
			
		||||
	app.get('/ping', ping);
 | 
			
		||||
	app.get('/sping', ping);
 | 
			
		||||
	app.get(relativePath + '/ping', ping);
 | 
			
		||||
	app.get(relativePath + '/sping', ping);
 | 
			
		||||
 | 
			
		||||
	setupFavicon(app);
 | 
			
		||||
 | 
			
		||||
@@ -231,6 +231,7 @@ function setupAutoLocale(app, callback) {
 | 
			
		||||
 | 
			
		||||
function listen(callback) {
 | 
			
		||||
	callback = callback || function () { };
 | 
			
		||||
	console.log('derp', nconf.get('port'));
 | 
			
		||||
	var port = parseInt(nconf.get('port'), 10);
 | 
			
		||||
	var isSocket = isNaN(port);
 | 
			
		||||
	var socketPath = isSocket ? nconf.get('port') : '';
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,7 @@ var groups = require('../src/groups');
 | 
			
		||||
var user = require('../src/user');
 | 
			
		||||
var meta = require('../src/meta');
 | 
			
		||||
var privileges = require('../src/privileges');
 | 
			
		||||
var helpers = require('./helpers');
 | 
			
		||||
 | 
			
		||||
describe('feeds', function () {
 | 
			
		||||
	var tid;
 | 
			
		||||
@@ -113,4 +114,81 @@ describe('feeds', function () {
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	describe('private feeds and tokens', function () {
 | 
			
		||||
		var jar;
 | 
			
		||||
		var rssToken;
 | 
			
		||||
		before(function (done) {
 | 
			
		||||
			helpers.loginUser('foo', 'barbar', function (err, _jar) {
 | 
			
		||||
				assert.ifError(err);
 | 
			
		||||
				jar = _jar;
 | 
			
		||||
				done();
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		it('should load feed if its not private', function (done) {
 | 
			
		||||
			request(nconf.get('url') + '/category/' + cid + '.rss', { }, function (err, res, body) {
 | 
			
		||||
				assert.ifError(err);
 | 
			
		||||
				assert.equal(res.statusCode, 200);
 | 
			
		||||
				assert(body);
 | 
			
		||||
				done();
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		it('should not allow access if uid or token is missing', function (done) {
 | 
			
		||||
			privileges.categories.rescind(['read'], cid, 'guests', function (err) {
 | 
			
		||||
				assert.ifError(err);
 | 
			
		||||
				async.parallel({
 | 
			
		||||
					test1: function (next) {
 | 
			
		||||
						request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid, { }, next);
 | 
			
		||||
					},
 | 
			
		||||
					test2: function (next) {
 | 
			
		||||
						request(nconf.get('url') + '/category/' + cid + '.rss?token=sometoken', { }, next);
 | 
			
		||||
					},
 | 
			
		||||
				}, function (err, results) {
 | 
			
		||||
					assert.ifError(err);
 | 
			
		||||
					assert.equal(results.test1[0].statusCode, 200);
 | 
			
		||||
					assert.equal(results.test2[0].statusCode, 200);
 | 
			
		||||
					assert(results.test1[0].body.indexOf('Login to your account') !== -1);
 | 
			
		||||
					assert(results.test2[0].body.indexOf('Login to your account') !== -1);
 | 
			
		||||
					done();
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		it('should not allow access if token is wrong', function (done) {
 | 
			
		||||
			request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid + '&token=sometoken', { }, function (err, res, body) {
 | 
			
		||||
				assert.ifError(err);
 | 
			
		||||
				assert.equal(res.statusCode, 200);
 | 
			
		||||
				assert(body.indexOf('Login to your account') !== -1);
 | 
			
		||||
				done();
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		it('should allow access if token is correct', function (done) {
 | 
			
		||||
			request(nconf.get('url') + '/api/category/' + cid, { jar: jar, json: true }, function (err, res, body) {
 | 
			
		||||
				assert.ifError(err);
 | 
			
		||||
				rssToken = body.rssFeedUrl.split('token')[1].slice(1);
 | 
			
		||||
				request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid + '&token=' + rssToken, { }, function (err, res, body) {
 | 
			
		||||
					assert.ifError(err);
 | 
			
		||||
					assert.equal(res.statusCode, 200);
 | 
			
		||||
					assert(body);
 | 
			
		||||
					done();
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
 | 
			
		||||
		it('should not allow access if token is correct but has no privilege', function (done) {
 | 
			
		||||
			privileges.categories.rescind(['read'], cid, 'registered-users', function (err) {
 | 
			
		||||
				assert.ifError(err);
 | 
			
		||||
				request(nconf.get('url') + '/category/' + cid + '.rss?uid=' + fooUid + '&token=' + rssToken, { }, function (err, res, body) {
 | 
			
		||||
					assert.ifError(err);
 | 
			
		||||
					assert.equal(res.statusCode, 200);
 | 
			
		||||
					assert(body.indexOf('Login to your account') !== -1);
 | 
			
		||||
					done();
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -110,7 +110,7 @@ before(function (done) {
 | 
			
		||||
			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') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
 | 
			
		||||
			nconf.set('port', urlObject.port || nconf.get('port') || (nconf.get('PORT_ENV_VAR') ? nconf.get(nconf.get('PORT_ENV_VAR')) : false) || 4567);
 | 
			
		||||
			nconf.set('upload_path', path.join(nconf.get('base_dir'), nconf.get('upload_path')));
 | 
			
		||||
 | 
			
		||||
			nconf.set('core_templates_path', path.join(__dirname, '../../src/views'));
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user