mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-17 14:00:29 +01:00
Compare commits
4 Commits
v1.13.2
...
protocol-v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
494447741a | ||
|
|
7b174d25cb | ||
|
|
bbb03a08e9 | ||
|
|
b884b0be01 |
71
CHANGELOG.md
71
CHANGELOG.md
@@ -1,74 +1,3 @@
|
||||
#### 1.13.1 (2019-12-19)
|
||||
|
||||
##### Chores
|
||||
|
||||
* incrementing version number - v1.13.1 (d1e0672f)
|
||||
* incrementing version number - v1.13.0 (c38b2d23)
|
||||
* **deps:**
|
||||
* update dependency husky to v3.1.0 (#8046) (c3418c26)
|
||||
* update dependency coveralls to v3.0.9 (#8067) (0aeee144)
|
||||
* update dependency eslint to v6.7.0 (32cfe96f)
|
||||
* update dependency coveralls to v3.0.8 (#8054) (8ba26104)
|
||||
|
||||
##### Documentation Changes
|
||||
|
||||
* updated changelog (94499da3)
|
||||
|
||||
##### New Features
|
||||
|
||||
* better output for cli plugins list, closes #8075 (4fc69443)
|
||||
* #5272, allow changing user groups from manage users page (05c9fe27)
|
||||
* merge social authentication into plugins menu in ACP (f9a8ebfc)
|
||||
* convert middleware.isAdmin to async/await (efd1e88b)
|
||||
|
||||
##### Bug Fixes
|
||||
|
||||
* #8085, fix cookie name (dec157d6)
|
||||
* #8058, fix incorrect digest setting display in ACP (1b992d82)
|
||||
* remove select version (6a17e32d)
|
||||
* travis config (3ae98300)
|
||||
* travis :dog: (3731dc4e)
|
||||
* #8078, dont mark notifications read without a mergeId (a8df6d62)
|
||||
* #8077, show continue chat on all profile pages (7af1c873)
|
||||
* profile showing posts from deleted topics (2679f37d)
|
||||
* #8073, configurable necroThreshold (4d669783)
|
||||
* allow members to search as well (b323df2f)
|
||||
* #8069, dont show hidden groups in search (c2cd7de8)
|
||||
* missing await (33fd4a1c)
|
||||
* #8064, break-word on post-queue (1bda92e3)
|
||||
* #6711 (7ed002a1)
|
||||
* #8061, don't crash if there is a network problem (de404102)
|
||||
* #8059, properly mark topic unread when using mark unread for all (a688aaae)
|
||||
* #8042, dont show errors after clearing form (3811e0a3)
|
||||
* unhandled promise rejection error on reset error (51073772)
|
||||
* #8050, fix redirect after registration (366ad5cd)
|
||||
* make _csrf a secure cookie if the website is using https (#8045) (0efe27b1)
|
||||
* #8034 (0a96c923)
|
||||
* serialize (a2545204)
|
||||
* show login fields if user has local password (1eca5b3d)
|
||||
* use the correct attribute name for widgets (6c404b81)
|
||||
* **deps:**
|
||||
* update dependency semver to v7 (483d7535)
|
||||
* update dependency nodebb-theme-vanilla to v11.1.12 (610ecf35)
|
||||
* update dependency sharp to v0.23.4 (#8076) (eb18c182)
|
||||
* update dependency nodebb-theme-persona to v10.1.30 (0514383a)
|
||||
* update dependency nodebb-plugin-markdown to v8.11.0 (702ca164)
|
||||
* update dependency connect-mongo to v3.2.0 (2aef7a5b)
|
||||
* update dependency mongodb to v3.3.5 (#8065) (68118e43)
|
||||
* update dependency nodebb-theme-persona to v10.1.29 (#8057) (34933091)
|
||||
* update dependency sharp to v0.23.3 (#8044) (6fa88823)
|
||||
* update dependency validator to v12.1.0 (#8055) (488ea394)
|
||||
* update dependency nodebb-theme-slick to v1.2.28 (#8041) (b3511f71)
|
||||
* update dependency nodebb-theme-vanilla to v11.1.11 (#8040) (d567c4ae)
|
||||
* update dependency nodebb-theme-persona to v10.1.28 (#8039) (6c87bed5)
|
||||
* update dependency nodebb-plugin-dbsearch to v4.0.7 (#8038) (1e2e16b4)
|
||||
|
||||
##### Refactors
|
||||
|
||||
* async/await middleware (a227cbe3)
|
||||
* change to const/let (3454a24b)
|
||||
* shorter returns (cec00795)
|
||||
|
||||
### 1.13.0 (2019-11-13)
|
||||
|
||||
##### Chores
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPL-3.0",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "1.13.2",
|
||||
"version": "1.13.0",
|
||||
"homepage": "http://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -49,7 +49,7 @@
|
||||
"connect-mongo": "3.2.0",
|
||||
"connect-multiparty": "^2.1.0",
|
||||
"connect-pg-simple": "^6.0.0",
|
||||
"connect-redis": "4.0.4",
|
||||
"connect-redis": "4.0.3",
|
||||
"cookie-parser": "^1.4.3",
|
||||
"cron": "^1.3.0",
|
||||
"cropperjs": "^1.2.2",
|
||||
@@ -63,7 +63,7 @@
|
||||
"helmet": "^3.11.0",
|
||||
"html-to-text": "^5.0.0",
|
||||
"ipaddr.js": "^1.5.4",
|
||||
"jquery": "3.4.1",
|
||||
"jquery": "^3.2.1",
|
||||
"jsesc": "2.5.2",
|
||||
"json-2-csv": "^3.0.0",
|
||||
"jsonwebtoken": "^8.4.0",
|
||||
@@ -73,43 +73,43 @@
|
||||
"lru-cache": "5.1.1",
|
||||
"material-design-lite": "^1.3.0",
|
||||
"mime": "^2.2.0",
|
||||
"mkdirp": "^1.0.3",
|
||||
"mongodb": "3.5.2",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mongodb": "3.4.0",
|
||||
"morgan": "^1.9.1",
|
||||
"mousetrap": "^1.6.1",
|
||||
"mubsub-nbb": "^1.5.1",
|
||||
"nconf": "^0.10.0",
|
||||
"nodebb-plugin-composer-default": "6.3.21",
|
||||
"nodebb-plugin-composer-default": "6.3.20",
|
||||
"nodebb-plugin-dbsearch": "4.0.7",
|
||||
"nodebb-plugin-emoji": "^3.0.0",
|
||||
"nodebb-plugin-emoji-android": "2.0.0",
|
||||
"nodebb-plugin-markdown": "8.11.0",
|
||||
"nodebb-plugin-mentions": "2.7.4",
|
||||
"nodebb-plugin-mentions": "2.7.3",
|
||||
"nodebb-plugin-soundpack-default": "1.0.0",
|
||||
"nodebb-plugin-spam-be-gone": "0.6.7",
|
||||
"nodebb-rewards-essentials": "0.1.2",
|
||||
"nodebb-theme-lavender": "5.0.11",
|
||||
"nodebb-theme-persona": "10.1.34",
|
||||
"nodebb-theme-persona": "10.1.30",
|
||||
"nodebb-theme-slick": "1.2.28",
|
||||
"nodebb-theme-vanilla": "11.1.15",
|
||||
"nodebb-widget-essentials": "4.0.18",
|
||||
"nodebb-theme-vanilla": "11.1.12",
|
||||
"nodebb-widget-essentials": "4.0.17",
|
||||
"nodemailer": "^6.0.0",
|
||||
"passport": "^0.4.0",
|
||||
"passport-local": "1.0.0",
|
||||
"pg": "^7.4.0",
|
||||
"pg-cursor": "^2.0.0",
|
||||
"postcss": "7.0.26",
|
||||
"postcss": "7.0.21",
|
||||
"postcss-clean": "1.1.0",
|
||||
"promise-polyfill": "^8.0.0",
|
||||
"prompt": "^1.0.0",
|
||||
"redis": "2.8.0",
|
||||
"request": "2.88.0",
|
||||
"rimraf": "3.0.1",
|
||||
"rimraf": "3.0.0",
|
||||
"rss": "^1.2.2",
|
||||
"sanitize-html": "^1.16.3",
|
||||
"semver": "^7.0.0",
|
||||
"serve-favicon": "^2.4.5",
|
||||
"sharp": "0.24.0",
|
||||
"sharp": "0.23.4",
|
||||
"sitemap": "^5.0.0",
|
||||
"socket.io": "2.3.0",
|
||||
"socket.io-adapter-cluster": "^1.0.1",
|
||||
@@ -124,27 +124,27 @@
|
||||
"textcomplete.contenteditable": "^0.1.1",
|
||||
"toobusy-js": "^0.5.1",
|
||||
"uglify-es": "^3.3.9",
|
||||
"validator": "12.2.0",
|
||||
"validator": "12.1.0",
|
||||
"winston": "3.2.1",
|
||||
"xml": "^1.0.1",
|
||||
"xregexp": "^4.1.1",
|
||||
"zxcvbn": "^4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "8.3.5",
|
||||
"@commitlint/config-angular": "8.3.4",
|
||||
"@commitlint/cli": "8.2.0",
|
||||
"@commitlint/config-angular": "8.2.0",
|
||||
"coveralls": "3.0.9",
|
||||
"eslint": "6.8.0",
|
||||
"eslint": "6.7.0",
|
||||
"eslint-config-airbnb-base": "14.0.0",
|
||||
"eslint-plugin-import": "2.18.2",
|
||||
"grunt": "1.0.4",
|
||||
"grunt-contrib-watch": "1.1.0",
|
||||
"husky": "4.2.1",
|
||||
"husky": "3.1.0",
|
||||
"jsdom": "15.2.1",
|
||||
"lint-staged": "10.0.7",
|
||||
"mocha": "7.0.1",
|
||||
"lint-staged": "9.4.2",
|
||||
"mocha": "6.2.2",
|
||||
"mocha-lcov-reporter": "1.3.0",
|
||||
"nyc": "15.0.0",
|
||||
"nyc": "14.1.1",
|
||||
"smtp-server": "3.5.0"
|
||||
},
|
||||
"bugs": {
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"manage/groups": "Gruppen",
|
||||
"manage/ip-blacklist": "IP Blacklist",
|
||||
"manage/uploads": "Uploads",
|
||||
"manage/digest": "Zusammenfassungen",
|
||||
"manage/digest": "Digests",
|
||||
|
||||
"section-settings": "Einstellungen",
|
||||
"settings/general": "Allgemein",
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
"testing.select": "Wählen Sie die E-Mail Vorlage",
|
||||
"testing.send": "Test-E-Mail versenden",
|
||||
"testing.send-help": "Die Test-E-Mail wird an die E-Mail Adresse des momentan eingeloggten Nutzers geschickt.",
|
||||
"subscriptions": "Email Zusammenfassungen",
|
||||
"subscriptions.disable": "Deaktivierung der Email Zusammenfassungen",
|
||||
"subscriptions": "Email Digests",
|
||||
"subscriptions.disable": "Disable email digests",
|
||||
"subscriptions.hour": "Sende Zeit",
|
||||
"subscriptions.hour-help": "Bitte geben Sie eine Nummer ein, welche die Stunde repräsentiert zu welcher geplante Emails versandt werden sollen (z.B. <code>0</code> für Mitternacht, <code>17</code> für 5 Uhr Nachmittags). Beachten Sie, dass die Zeit auf der Serverzeit basiert und daher nicht umbedingt mit ihrer Systemzeit übereinstimmen muss.<br>Die ungefähre Serverzeit ist: <span id=\"serverTime\"></span><br>Die nächste tägliche Sendung ist um <span id=\"nextDigestTime\"></span> geplant"
|
||||
}
|
||||
@@ -27,9 +27,9 @@
|
||||
"digest.week": "der letzten Woche",
|
||||
"digest.month": "des letzen Monats",
|
||||
"digest.subject": "Zusammenfassung für %1",
|
||||
"digest.title.day": "Deine tägliche Zusammenfassung",
|
||||
"digest.title.week": "Deine wöchentliche Zusammenfassung",
|
||||
"digest.title.month": "Deine monatliche Zusammenfassung",
|
||||
"digest.title.day": "Your Daily Digest",
|
||||
"digest.title.week": "Your Weekly Digest",
|
||||
"digest.title.month": "Your Monthly Digest",
|
||||
"notif.chat.subject": "Neue Chatnachricht von %1 erhalten",
|
||||
"notif.chat.cta": "Klicke hier, um die Unterhaltung fortzusetzen",
|
||||
"notif.chat.unsub.info": "Diese Chat-Benachrichtigung wurde dir aufgrund deiner Abonnement-Einstellungen gesendet.",
|
||||
|
||||
@@ -134,6 +134,6 @@
|
||||
"diffs.no-revisions-description": "Dieser Beitrag ha <strong>%1</strong> Revisionen.",
|
||||
"diffs.current-revision": "Aktuelle Revision",
|
||||
"diffs.original-revision": "Ursprüngliche Revision",
|
||||
"timeago_later": "%1 später",
|
||||
"timeago_later": "%1 later",
|
||||
"timeago_earlier": "%1 earlier"
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
{
|
||||
"events": "ארועים",
|
||||
"no-events": "אין ארועים",
|
||||
"control-panel": "בקרת ארועים\n ",
|
||||
"filters": "מסננים",
|
||||
"filters-apply": "החל מסננים",
|
||||
"filter-type": "סוג אירוע",
|
||||
"filter-start": "מתאריך",
|
||||
"filter-end": "עד תאריך",
|
||||
"filter-perPage": "פריטים בכל דף"
|
||||
"control-panel": "בקרת ארועים",
|
||||
"filters": "Filters",
|
||||
"filters-apply": "Apply Filters",
|
||||
"filter-type": "Event Type",
|
||||
"filter-start": "Start Date",
|
||||
"filter-end": "End Date",
|
||||
"filter-perPage": "Per Page"
|
||||
}
|
||||
@@ -18,13 +18,13 @@
|
||||
"last_reply_time": "תגובה אחרונה",
|
||||
"reply-as-topic": "הגב כנושא",
|
||||
"guest-login-reply": "התחבר כדי לפרסם תגובה",
|
||||
"login-to-view": "🔒 התחבר כדי לצפות",
|
||||
"login-to-view": "🔒 Log in to view",
|
||||
"edit": "עריכה",
|
||||
"delete": "מחק",
|
||||
"purge": "מחק לצמיתות",
|
||||
"restore": "שחזר",
|
||||
"move": "הזז",
|
||||
"change-owner": "שנה מחבר הודעה",
|
||||
"change-owner": "Change Owner",
|
||||
"fork": "פורק",
|
||||
"link": "לינק",
|
||||
"share": "שתף",
|
||||
@@ -56,7 +56,7 @@
|
||||
"ignoring": "מתעלם",
|
||||
"watching.description": "הודע לי על תגובות חדשות. <br/>הצג נושא חדש ברשימת הלא נקראו.",
|
||||
"not-watching.description": "אל תיידע אותי על תגובות חדשות. <br/>הצג נושא חדש ברשימת הלא נקראו במידה ובחרתי לא להתעלם מקבוצת הדיון",
|
||||
"ignoring.description": "אל תתריע לי על תגובות חדשות. <br/>אל תראה את הנושא בנושאים שלא נקראו ",
|
||||
"ignoring.description": "Do not notify me of new replies.<br/>Do not show topic in unread.",
|
||||
"thread_tools.title": "כלי נושא",
|
||||
"thread_tools.markAsUnreadForAll": "סמן לא נקרא לכולם",
|
||||
"thread_tools.pin": "נעץ נושא",
|
||||
@@ -66,7 +66,7 @@
|
||||
"thread_tools.move": "הזז נושא",
|
||||
"thread_tools.move-posts": "הזז פוסטים",
|
||||
"thread_tools.move_all": "הזז הכל",
|
||||
"thread_tools.change_owner": "שנה את כותב ההודעה",
|
||||
"thread_tools.change_owner": "Change Owner",
|
||||
"thread_tools.select_category": "בחר קטגוריה",
|
||||
"thread_tools.fork": "שכפל נושא",
|
||||
"thread_tools.delete": "מחק נושא",
|
||||
@@ -101,7 +101,7 @@
|
||||
"delete_posts_instruction": "לחץ על הפוסטים שברצונך למחוק",
|
||||
"merge_topics_instruction": "לחץ על הנושאים שתרצה למזג",
|
||||
"move_posts_instruction": "לחץ על הפוסטים שאתה רוצה להזיז",
|
||||
"change_owner_instruction": "לחץ על ההודעה שהנך רוצה לשנות את בעל ההודעה",
|
||||
"change_owner_instruction": "Click the posts you want to assign to another user",
|
||||
"composer.title_placeholder": "הכנס את כותרת הנושא כאן...",
|
||||
"composer.handle_placeholder": "שם",
|
||||
"composer.discard": "ביטול",
|
||||
@@ -130,10 +130,10 @@
|
||||
"stale.reply_anyway": "הגב לנושא זה בכל מקרה",
|
||||
"link_back": "תגובה: [%1](%2)",
|
||||
"diffs.title": "היסטוריית עריכת הפוסט",
|
||||
"diffs.description": "להודעה זו יש <strong>%1</strong> עריכות. לחץ על אחת מהעריכות להלן כדי לראות את תוכן ההודעה בנקודת זמן זו.",
|
||||
"diffs.description": "This post has <strong>%1</strong> revisions. Click one of the revisions below to see the post content at that point in time.",
|
||||
"diffs.no-revisions-description": "לפוסט זה יש <strong>%1</strong>גרסאות",
|
||||
"diffs.current-revision": "גירסה נוכחית",
|
||||
"diffs.original-revision": "גירסה מקורית",
|
||||
"timeago_later": "אחרי %1:",
|
||||
"timeago_earlier": "לפני %1 "
|
||||
"diffs.current-revision": "current revision",
|
||||
"diffs.original-revision": "original revision",
|
||||
"timeago_later": "%1 later",
|
||||
"timeago_earlier": "%1 earlier"
|
||||
}
|
||||
@@ -30,16 +30,16 @@
|
||||
"select-category": "Seleziona Categoria",
|
||||
"set-parent-category": "Imposta la Categoria Padre",
|
||||
|
||||
"privileges.description": "In questa sezione è possibile configurare i privilegi di controllo dell'accesso per parti del sito. I privilegi possono essere concessi per utente o per gruppo. Seleziona il dominio dell'effetto dal menu a discesa in basso.",
|
||||
"privileges.description": "You can configure the access control privileges for portions of the site in this section. Privileges can be granted on a per-user or a per-group basis. Select the domain of effect from the dropdown below.",
|
||||
"privileges.category-selector": "Configura privilegi per",
|
||||
"privileges.warning": "<strong>Nota</strong>: Le impostazioni dei privilegi hanno effetto immediato. Non è necessario salvare la categoria dopo aver regolato queste impostazioni.",
|
||||
"privileges.warning": "<strong>Note</strong>: Privilege settings take effect immediately. It is not necessary to save the category after adjusting these settings.",
|
||||
"privileges.section-viewing": "Visualizzazione dei Privilegi",
|
||||
"privileges.section-posting": "Privilegi di pubblicazione",
|
||||
"privileges.section-moderation": "Privilegi di Moderazione",
|
||||
"privileges.section-posting": "Posting Privileges",
|
||||
"privileges.section-moderation": "Moderation Privileges",
|
||||
"privileges.section-other": "Altro",
|
||||
"privileges.section-user": "Utente",
|
||||
"privileges.search-user": "Aggiungi Utente",
|
||||
"privileges.no-users": "Nessun privilegio specifico dell'utente in questa categoria.",
|
||||
"privileges.no-users": "No user-specific privileges in this category.",
|
||||
"privileges.section-group": "Gruppo",
|
||||
"privileges.group-private": "Questo gruppo è privato",
|
||||
"privileges.search-group": "Aggiungi gruppo",
|
||||
@@ -49,27 +49,27 @@
|
||||
"privileges.copy-group-privileges-to-children": "Copia i privilegi di questo gruppo dai figli di questa categoria.",
|
||||
"privileges.copy-group-privileges-to-all-categories": "Copia i privilegi di questo gruppo in tutte le categorie.",
|
||||
"privileges.copy-group-privileges-from": "Copia questo gruppo di privilegi da un altra categoria.",
|
||||
"privileges.inherit": "Se l' <code>utente registrato</code> al gruppo viene concesso un privilegio specifico, tutti gli altri gruppi ricevono un<strong>privilegio implicito</strong>,anche se non sono esplicitamente definiti / controllati. Questo privilegio implicito ti viene mostrato perché tutti gli utenti fanno parte di<code>gruppo di utenti</code> registrati e quindi i privilegi per gruppi aggiuntivi non devono essere esplicitamente concessi.",
|
||||
"privileges.inherit": "If the <code>registered-users</code> group is granted a specific privilege, all other groups receive an <strong>implicit privilege</strong>, even if they are not explicitly defined/checked. This implicit privilege is shown to you because all users are part of the <code>registered-users</code> user group, and so, privileges for additional groups need not be explicitly granted.",
|
||||
"privileges.copy-success": "Privilegi copiati!",
|
||||
|
||||
"analytics.back": "Torna all'Elenco delle Categorie",
|
||||
"analytics.back": "Back to Categories List",
|
||||
"analytics.title": "Statistiche per la categoria \"%1\"",
|
||||
"analytics.pageviews-hourly": "<strong>Figura 1</strong> – Vista delle visualizzazioni orarie per questa categoria</small>",
|
||||
"analytics.pageviews-daily": "<strong>Figura 2</strong> – Vista delle visualizzazioni giornaliere per questa categoria</small>",
|
||||
"analytics.topics-daily": "<strong>Figura 3</strong> – Argomenti giornalieri creati in questa categoria</small>",
|
||||
"analytics.posts-daily": "<strong>Figura 4</strong>dash; Post giornalieri pubblicati in questa categoria</small>",
|
||||
"analytics.posts-daily": "<strong>Figure 4</strong> – Daily posts made in this category</small>",
|
||||
|
||||
"alert.created": "Creato",
|
||||
"alert.create-success": "Categoria creata con successo!",
|
||||
"alert.none-active": "Hai una categoria non attiva.",
|
||||
"alert.create": "Crea una Categoria",
|
||||
"alert.confirm-moderate": "<strong>Sei sicuro di voler concedere il privilegio di moderazione a questo gruppo di utenti?</strong> Questo gruppo è pubblico e tutti gli utenti possono aderire a piacimento.",
|
||||
"alert.confirm-moderate": "<strong>Are you sure you wish to grant the moderation privilege to this user group?</strong> This group is public, and any users can join at will.",
|
||||
"alert.confirm-purge": "<p class=\"lead\">Vuoi davvero eliminare definitivamente questa categoria \"%1\"?</p><h5><strong class=\"text-danger\">Attenzione!</strong>Tutte le discussioni e i posti in questa categoria saranno eliminati definitivamente!</h5> <p class=\"help-block\">Eliminare definitivamente una categoria rimuoverà tutte le discussioni e i post ed eliminerà la categoria dal database. Se vuoi rimuovere una categoria <em>temporaneamente</em>, puoi invece \"disabilitare\" la categoria.",
|
||||
"alert.purge-success": "Categoria eliminata definitivamente!",
|
||||
"alert.copy-success": "Impostazioni copiate!",
|
||||
"alert.set-parent-category": "Imposta la Categoria Genitore",
|
||||
"alert.updated": "Categorie aggiornate",
|
||||
"alert.updated-success": "ID categoria %1 aggiornati correttamente.",
|
||||
"alert.copy-success": "Settings Copied!",
|
||||
"alert.set-parent-category": "Set Parent Category",
|
||||
"alert.updated": "Updated Categories",
|
||||
"alert.updated-success": "Category IDs %1 successfully updated.",
|
||||
"alert.upload-image": "Carica immagine categoria",
|
||||
"alert.find-user": "Trova un Utente",
|
||||
"alert.user-search": "Cerca un utente qui...",
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"lead": "Di seguito viene visualizzato un elenco di statistiche e tempi di consegna del digest.",
|
||||
"disclaimer": "Si informa che la consegna della posta non è garantita a causa della natura della tecnologia di posta elettronica. Molte variabili determinano se una e-mail inviata al server del destinatario viene infine recapitata nella posta in arrivo dell'utente, inclusa la reputazione del server, gli indirizzi IP nella lista nera e se DKIM/SPF/DMARC è configurato.",
|
||||
"disclaimer-continued": "Una consegna corretta indica che il messaggio è stato inviato correttamente da NodeBB e riconosciuto dal server destinatario. Non significa che l'email è arrivata nella posta in arrivo. Per risultati ottimali, si consiglia di utilizzare un servizio di consegna e-mail di terze parti come <a href=\"https://sendgrid.com/why-sendgrid/\">SendGrid</a>.",
|
||||
"lead": "A listing of digest delivery stats and times is displayed below.",
|
||||
"disclaimer": "Please be advised that email delivery is not guaranteed, due to the nature of email technology. Many variables factor into whether an email sent to the recipient server is ultimately delivered into the user's inbox, including server reputation, blacklisted IP addresses, and whether DKIM/SPF/DMARC is configured.",
|
||||
"disclaimer-continued": "A successful delivery means the message was sent successfully by NodeBB and acknowledged by the recipient server. It does not mean the email landed in the inbox. For best results, we recommend using a third-party email delivery service such as <a href=\"https://sendgrid.com/why-sendgrid/\">SendGrid</a>.",
|
||||
|
||||
"user": "Utente",
|
||||
"subscription": "Tipo di Abbonamento",
|
||||
"last-delivery": "Ultima consegna riuscita",
|
||||
"default": "Sistema predefinito",
|
||||
"default-help": "<em>Sistema predefinito</em> significa che l'utente non ha esplicitamente sovrascritto l'impostazione del forum globale per i digest, che attualmente è: & quot;<strong>%1</strong>"",
|
||||
"resend": "Rinvia Digest",
|
||||
"resend-all-confirm": "Sei sicuro di voler eseguire manualmente questa corsa digest?",
|
||||
"resent-single": "Invio del digest manuale completato",
|
||||
"resent-day": "Rinvio digest giornaliero",
|
||||
"resent-week": "Rinvio del digest settimanale",
|
||||
"resent-month": "Rinvio del digest mensile",
|
||||
"null": "<em>Mai</em>",
|
||||
"manual-run": "Esecuzione digest manuale:",
|
||||
"user": "User",
|
||||
"subscription": "Subscription Type",
|
||||
"last-delivery": "Last successful delivery",
|
||||
"default": "System default",
|
||||
"default-help": "<em>System default</em> means the user has not explicitly overridden the global forum setting for digests, which is currently: "<strong>%1</strong>"",
|
||||
"resend": "Resend Digest",
|
||||
"resend-all-confirm": "Are you sure you wish to mnually execute this digest run?",
|
||||
"resent-single": "Manual digest resend completed",
|
||||
"resent-day": "Daily digest resent",
|
||||
"resent-week": "Weekly digest resent",
|
||||
"resent-month": "Monthly digest resent",
|
||||
"null": "<em>Never</em>",
|
||||
"manual-run": "Manual digest run:",
|
||||
|
||||
"no-delivery-data": "Nessun dato di consegna trovato"
|
||||
"no-delivery-data": "No delivery data found"
|
||||
}
|
||||
@@ -15,8 +15,8 @@
|
||||
"delete": "Rimuovi Utente(i)",
|
||||
"purge": "Rimuovi Utente(i) e Contenuto",
|
||||
"download-csv": "Scarica CSV",
|
||||
"manage-groups": "Gestisci Gruppi",
|
||||
"add-group": "Aggiungi Gruppo",
|
||||
"manage-groups": "Manage Groups",
|
||||
"add-group": "Add Group",
|
||||
"invite": "Invito",
|
||||
"new": "Nuovo utente",
|
||||
|
||||
|
||||
@@ -27,33 +27,33 @@
|
||||
"restrictions.max-title-length": "Lunghezza Massima Titolo",
|
||||
"restrictions.min-post-length": "Lunghezza Minima Post",
|
||||
"restrictions.max-post-length": "Lunghezza Massima Post",
|
||||
"restrictions.days-until-stale": "Giorni prima che l'argomento sia considerato vecchio",
|
||||
"restrictions.stale-help": "Se un argomento è considerato \"non aggiornato\", verrà mostrato un avviso agli utenti che tentano di rispondere a tale argomento.",
|
||||
"timestamp": "Data e Ora",
|
||||
"timestamp.cut-off": "Data di interruzione (in giorni)",
|
||||
"timestamp.cut-off-help": "I tempi delle date verranno visualizzati in modo relativo (ad es. \"3 ore fa\" / \"5 giorni fa\") e localizzati in varie\n\t\t\t\t\tlingue. Dopo un certo punto, questo testo può essere cambiato per visualizzare la data localizzata\n\t\t\t\t\t(es. 5 Nov 2016 15:30).<br /><em>(Predefinito: <code>30</code>, o un mese).Impostare su 0 per visualizzare sempre le date, lasciare vuoto per visualizzare sempre i tempi relativi.</em>",
|
||||
"timestamp.necro-threshold": "Necro Threshold (in giorni)",
|
||||
"timestamp.necro-threshold-help": "Un messaggio verrà mostrato tra i post se il tempo tra loro è più lungo della soglia necro. (Predefinito: <code>7</code>, o una settimana). Impostare su 0 per disabilitare.</em>",
|
||||
"teaser": "Post Inopportuno",
|
||||
"teaser.last-post": "Ultimo – Mostra l'ultimo post, incluso il post originale, se non ci sono risposte",
|
||||
"teaser.last-reply": "Ultimo – Mostra l'ultima risposta o un segnaposto \"Nessuna risposta\" se non risposto",
|
||||
"restrictions.days-until-stale": "Days until topic is considered stale",
|
||||
"restrictions.stale-help": "If a topic is considered \"stale\", then a warning will be shown to users who attempt to reply to that topic.",
|
||||
"timestamp": "Timestamp",
|
||||
"timestamp.cut-off": "Date cut-off (in days)",
|
||||
"timestamp.cut-off-help": "Dates & times will be shown in a relative manner (e.g. \"3 hours ago\" / \"5 days ago\"), and localised into various\n\t\t\t\t\tlanguages. After a certain point, this text can be switched to display the localised date itself\n\t\t\t\t\t(e.g. 5 Nov 2016 15:30).<br /><em>(Default: <code>30</code>, or one month). Set to 0 to always display dates, leave blank to always display relative times.</em>",
|
||||
"timestamp.necro-threshold": "Necro Threshold (in days)",
|
||||
"timestamp.necro-threshold-help": "A message will be shown between posts if the time between them is longer than the necro threshold. (Default: <code>7</code>, or one week). Set to 0 to disable.</em>",
|
||||
"teaser": "Teaser Post",
|
||||
"teaser.last-post": "Last – Show the latest post, including the original post, if no replies",
|
||||
"teaser.last-reply": "Last – Show the latest reply, or a \"No replies\" placeholder if no replies",
|
||||
"teaser.first": "Primo",
|
||||
"unread": "Impostazioni non Lette",
|
||||
"unread.cutoff": "Giorni di interruzione non letti",
|
||||
"unread.min-track-last": "Post minimi nell'argomento prima del monitoraggio dell'ultima lettura",
|
||||
"unread": "Unread Settings",
|
||||
"unread.cutoff": "Unread cutoff days",
|
||||
"unread.min-track-last": "Minimum posts in topic before tracking last read",
|
||||
"recent": "Impostazioni Recenti",
|
||||
"recent.categoryFilter.disable": "Disabilita il filtro degli argomenti nelle categorie ignorate nella pagina/recente",
|
||||
"signature": "Impostazioni della Firma",
|
||||
"signature.disable": "Disabilita le firme",
|
||||
"signature.no-links": "Disabilita i collegamenti nelle firme",
|
||||
"signature.no-images": "Disabilita le immagini nelle firme",
|
||||
"signature.max-length": "Lunghezza massima della firma",
|
||||
"composer": "Impostazioni del compositore",
|
||||
"composer-help": "Le seguenti impostazioni regolano la funzionalità e/o l'aspetto del post compositore mostrato\n\t\t\t\tagli utenti quando creano nuovi argomenti o rispondono ad argomenti esistenti.",
|
||||
"composer.show-help": "Mostra la scheda \"Aiuto\"",
|
||||
"composer.enable-plugin-help": "Consenti ai plug-in di aggiungere contenuti alla scheda Guida",
|
||||
"composer.custom-help": "Testo di aiuto personalizzato",
|
||||
"ip-tracking": "Monitoraggio IP",
|
||||
"ip-tracking.each-post": "Traccia l'indirizzo IP per ogni post",
|
||||
"enable-post-history": "Abilita Cronologia post"
|
||||
"recent.categoryFilter.disable": "Disable filtering of topics in ignored categories on the /recent page",
|
||||
"signature": "Signature Settings",
|
||||
"signature.disable": "Disable signatures",
|
||||
"signature.no-links": "Disable links in signatures",
|
||||
"signature.no-images": "Disable images in signatures",
|
||||
"signature.max-length": "Maximum Signature Length",
|
||||
"composer": "Composer Settings",
|
||||
"composer-help": "The following settings govern the functionality and/or appearance of the post composer shown\n\t\t\t\tto users when they create new topics, or reply to existing topics.",
|
||||
"composer.show-help": "Show \"Help\" tab",
|
||||
"composer.enable-plugin-help": "Allow plugins to add content to the help tab",
|
||||
"composer.custom-help": "Custom Help Text",
|
||||
"ip-tracking": "IP Tracking",
|
||||
"ip-tracking.each-post": "Track IP Address for each post",
|
||||
"enable-post-history": "Enable Post History"
|
||||
}
|
||||
@@ -41,11 +41,11 @@
|
||||
"registration-type.invite-only": "Solo Invito",
|
||||
"registration-type.admin-invite-only": "Solo invito per Amministratori",
|
||||
"registration-type.disabled": "Niente registrazione",
|
||||
"registration-type.help": "Normale: gli utenti possono registrarsi dalla pagina/di registrazione.<br/>\nSolo invito: gli utenti possono invitare altri dalla <a href=\"%1/users\" target=\"_blank\">pagina</a> utenti.<br/>\nSolo su invito amministratore: solo gli amministratori possono invitare altri <a href=\"%1/users\" target=\"_blank\">utenti</a> e<a href=\"%1/admin/manage/users\">dalle pagine</a> amministratore/gestione/utenti.<br/>\nNessuna registrazione - Nessuna registrazione dell'utente.<br/>",
|
||||
"registration-approval-type.help": "Normale: gli utenti vengono registrati immediatamente.<br/>\nApprovazione amministratore - Le registrazioni degli utenti vengono inserite in una <a href=\"%1/admin/manage/registration\">coda di approvazione</a> per amministratori.<br/>\nApprovazione amministratore per IP - Normale per i nuovi utenti, Approvazione amministratore per indirizzi IP che dispongono già di un account.<br/>",
|
||||
"registration-type.help": "Normal - Users can register from the /register page.<br/>\nInvite Only - Users can invite others from the <a href=\"%1/users\" target=\"_blank\">users</a> page.<br/>\nAdmin Invite Only - Only administrators can invite others from <a href=\"%1/users\" target=\"_blank\">users</a> and <a href=\"%1/admin/manage/users\">admin/manage/users</a> pages.<br/>\nNo registration - No user registration.<br/>",
|
||||
"registration-approval-type.help": "Normal - Users are registered immediately.<br/>\nAdmin Approval - User registrations are placed in an <a href=\"%1/admin/manage/registration\">approval queue</a> for administrators.<br/>\nAdmin Approval for IPs - Normal for new users, Admin Approval for IP addresses that already have an account.<br/>",
|
||||
"registration.max-invites": "Numero massimo di inviti per Utente",
|
||||
"max-invites": "Numero massimo di inviti per Utente",
|
||||
"max-invites-help": "0 per nessuna restrizione. Gli amministratori ricevono infiniti inviti<br>Applicabile solo per \"Solo invito\"",
|
||||
"max-invites-help": "0 for no restriction. Admins get infinite invitations<br>Only applicable for \"Invite Only\"",
|
||||
"invite-expiration": "Invito scaduto",
|
||||
"invite-expiration-help": "Il tuo invito scadrà tra %1 giorni.",
|
||||
"min-username-length": "Lunghezza Minima Username",
|
||||
@@ -63,7 +63,7 @@
|
||||
"outgoing-new-tab": "Apri link esterni in una nuova scheda",
|
||||
"topic-search": "Abilita ricerca nella Discussione",
|
||||
"digest-freq": "Iscriviti al Riepilogo",
|
||||
"digest-freq.off": "Spento",
|
||||
"digest-freq.off": "Off",
|
||||
"digest-freq.daily": "Quotidiano",
|
||||
"digest-freq.weekly": "Settimanale",
|
||||
"digest-freq.monthly": "Mensile",
|
||||
@@ -72,8 +72,8 @@
|
||||
"follow-created-topics": "Segui le discussioni che tu crei",
|
||||
"follow-replied-topics": "Segui discussioni a cui rispondi tu",
|
||||
"default-notification-settings": "Impostazioni di notifica predefinite",
|
||||
"categoryWatchState": "Stato predefinito della categoria di controllo",
|
||||
"categoryWatchState.watching": "Guardare",
|
||||
"categoryWatchState.notwatching": "Non Guardare",
|
||||
"categoryWatchState": "Default category watch state",
|
||||
"categoryWatchState.watching": "Watching",
|
||||
"categoryWatchState.notwatching": "Not Watching",
|
||||
"categoryWatchState.ignoring": "Ignorato"
|
||||
}
|
||||
@@ -34,7 +34,7 @@
|
||||
"notif.chat.cta": "Clicca qui per continuare la conversazione",
|
||||
"notif.chat.unsub.info": "Questa notifica di chat ti è stata inviata perché l'hai sottoscritta nelle impostazioni.",
|
||||
"notif.post.unsub.info": "Questa notifica di discussione ti è stata inviata perché l'hai sottoscritta nelle impostazioni.",
|
||||
"notif.post.unsub.one-click": "In alternativa, annulla l'iscrizione a future email come questa, facendo clic",
|
||||
"notif.post.unsub.one-click": "Alternatively, unsubscribe from future emails like this, by clicking",
|
||||
"notif.cta": "Al forum",
|
||||
"notif.cta-new-reply": "Visualizza Post",
|
||||
"notif.cta-new-chat": "Visualizza Chat",
|
||||
@@ -42,8 +42,8 @@
|
||||
"notif.test.long": "Questo è un test delle notifiche email. Invia aiuto!",
|
||||
"test.text1": "Questa è una email di prova per verificare che il servizio di invio email è configurato correttamente sul tuo NodeBB.",
|
||||
"unsub.cta": "Clicca qui per modificare queste impostazioni",
|
||||
"unsubscribe": "Annulla l'iscrizione",
|
||||
"unsub.success": "Non riceverai più email dalla <strong>%1</strong> mailing list",
|
||||
"unsubscribe": "unsubscribe",
|
||||
"unsub.success": "You will no longer receive emails from the <strong>%1</strong> mailing list",
|
||||
"banned.subject": "Sei stato bannato da %1",
|
||||
"banned.text1": "L'utente %1 è stato bannato da %2",
|
||||
"banned.text2": "Questo ban durerà fino a %1.",
|
||||
|
||||
@@ -26,14 +26,14 @@
|
||||
"invalid-pagination-value": "Valore di impaginazione non valido, deve essere almeno %1 ed al massimo %2",
|
||||
"username-taken": "Nome utente già esistente",
|
||||
"email-taken": "Email già esistente",
|
||||
"email-not-confirmed": "Non puoi pubblicare post finché la tua email non è confermata, fai clic qui per confermare la tua email",
|
||||
"email-not-confirmed": "You are unable to post until your email is confirmed, please click here to confirm your email.",
|
||||
"email-not-confirmed-chat": "Non puoi chattare finché non confermi la tua email, per favore clicca qui per confermare la tua email.",
|
||||
"email-not-confirmed-email-sent": "La tua email non è stata ancora confermata, controlla la tua casella di posta per l'email di conferma. Non potrai pubblicare post o chattare fino a quando la tua email non sarà confermata.",
|
||||
"email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.",
|
||||
"no-email-to-confirm": "Questo forum richiede una conferma via email, clicca qui per inserire un'email",
|
||||
"email-confirm-failed": "Non abbiamo potuto confermare la tua email, per favore riprovaci più tardi.",
|
||||
"confirm-email-already-sent": "Email di conferma già inviata, per favore attendere %1 minuto(i) per inviarne un'altra.",
|
||||
"sendmail-not-found": "Impossibile trovare l'eseguibile di sendmail, per favore assicurati che sia installato ed eseguibile dall'utente che esegue NodeBB.",
|
||||
"digest-not-enabled": "Questo utente non ha digest attivi o l'impostazione predefinita del sistema non è configurata per l'invio di digest",
|
||||
"digest-not-enabled": "This user does not have digests enabled, or the system default is not configured to send digests",
|
||||
"username-too-short": "Nome utente troppo corto",
|
||||
"username-too-long": "Nome utente troppo lungo",
|
||||
"password-too-long": "Password troppo lunga",
|
||||
|
||||
@@ -159,17 +159,17 @@
|
||||
"consent.email_intro": "Occasionalmente, potremmo inviare email al tuo indirizzo email registrato per fornirti aggiornamenti e/o per informarti di nuove attività che ti riguardano. Puoi personalizzare la frequenza del riepilogo della comunità (compresa la disabilitazione definitiva), così come selezionare quali tipi di notifiche ricevere via email, tramite la pagina delle impostazioni utente.",
|
||||
"consent.digest_frequency": "A meno che non sia stato modificato esplicitamente nelle impostazioni utente, questa comunità fornisce email riepilogative ogni %1.",
|
||||
"consent.digest_off": "A meno che non sia stato modificato esplicitamente nelle impostazioni utente, questa comunità non invia email riepilogative",
|
||||
"consent.received": "Hai fornito il consenso a questo sito Web per raccogliere ed elaborare le tue informazioni. Non è richiesta alcuna azione aggiuntiva.",
|
||||
"consent.not_received": "Non hai fornito il consenso per la raccolta e l'elaborazione dei dati. In qualsiasi momento l'amministrazione di questo sito Web può decidere di eliminare il tuo account per renderlo conforme al regolamento generale sulla protezione dei dati.",
|
||||
"consent.received": "You have provided consent for this website to collect and process your information. No additional action is required.",
|
||||
"consent.not_received": "You have not provided consent for data collection and processing. At any time this website's administration may elect to delete your account in order to become compliant with the General Data Protection Regulation.",
|
||||
"consent.give": "Consenti",
|
||||
"consent.right_of_access": "Hai i privilegi di accesso",
|
||||
"consent.right_of_access_description": "Hai il diritto di accedere a tutti i dati raccolti da questo sito Web su richiesta. È possibile recuperare una copia di questi dati facendo clic sul pulsante appropriato di seguito.",
|
||||
"consent.right_of_access_description": "You have the right to access any data collected by this website upon request. You can retrieve a copy of this data by clicking the appropriate button below.",
|
||||
"consent.right_to_rectification": "Hai i privilegi alla rettifica",
|
||||
"consent.right_to_rectification_description": "Hai il diritto di modificare o aggiornare i dati inesatti forniti a noi. Il tuo profilo può essere aggiornato modificando il tuo profilo e il contenuto dei post può sempre essere modificato. In caso contrario, contattare questo team amministrativo del sito.",
|
||||
"consent.right_to_rectification_description": "You have the right to change or update any inaccurate data provided to us. Your profile can be updated by editing your profile, and post content can always be edited. If this is not the case, please contact this site's administrative team.",
|
||||
"consent.right_to_erasure": "Hai i privilegi per cancellare",
|
||||
"consent.right_to_erasure_description": "In qualsiasi momento, puoi revocare il tuo consenso alla raccolta e / o al trattamento dei dati eliminando il tuo account. Il tuo profilo individuale può essere eliminato, anche se i contenuti pubblicati rimarranno. Se desideri eliminare entrambi i tuoi account <strong>e</strong> i tuoi contenuti, contatta il team amministrativo per questo sito Web.",
|
||||
"consent.right_to_erasure_description": "At any time, you are able to revoke your consent to data collection and/or processing by deleting your account. Your individual profile can be deleted, although your posted content will remain. If you wish to delete both your account <strong>and</strong> your content, please contact the administrative team for this website.",
|
||||
"consent.right_to_data_portability": "Hai i privilegi alla portabilità dei dati",
|
||||
"consent.right_to_data_portability_description": "Puoi richiedere da noi un'esportazione leggibile meccanicamente di tutti i dati raccolti su di te e sul tuo account. Puoi farlo facendo clic sul pulsante appropriato in basso.",
|
||||
"consent.right_to_data_portability_description": "You may request from us a machine-readable export of any collected data about you and your account. You can do so by clicking the appropriate button below.",
|
||||
"consent.export_profile": "Esporta i profili (.csv)",
|
||||
"consent.export_uploads": "Esporta i contenuti caricati (.zip)",
|
||||
"consent.export_posts": "Esporta i post (.csv)"
|
||||
|
||||
@@ -20,11 +20,11 @@
|
||||
|
||||
"error.select-clone": "Proszę wybrać stronę do sklonowania",
|
||||
|
||||
"title": "Tytuł",
|
||||
"title.placeholder": "Tytuł (wyświetlany tylko w niektórych kontenerach)",
|
||||
"container": "Kontener",
|
||||
"container.placeholder": "Przeciągnij i upuść kontener lub wpisz tutaj HTML.",
|
||||
"show-to-groups": "Pokaż dla grup",
|
||||
"hide-from-groups": "Ukryj dla grup",
|
||||
"hide-on-mobile": "Ukraj na urządzeniach mobilnych"
|
||||
"title": "Title",
|
||||
"title.placeholder": "Title (only shown on some containers)",
|
||||
"container": "Container",
|
||||
"container.placeholder": "Drag and drop a container or enter HTML here.",
|
||||
"show-to-groups": "Show to groups",
|
||||
"hide-from-groups": "Hide from groups",
|
||||
"hide-on-mobile": "Hide on mobile"
|
||||
}
|
||||
@@ -3,19 +3,19 @@
|
||||
"disclaimer": "Please be advised that email delivery is not guaranteed, due to the nature of email technology. Many variables factor into whether an email sent to the recipient server is ultimately delivered into the user's inbox, including server reputation, blacklisted IP addresses, and whether DKIM/SPF/DMARC is configured.",
|
||||
"disclaimer-continued": "A successful delivery means the message was sent successfully by NodeBB and acknowledged by the recipient server. It does not mean the email landed in the inbox. For best results, we recommend using a third-party email delivery service such as <a href=\"https://sendgrid.com/why-sendgrid/\">SendGrid</a>.",
|
||||
|
||||
"user": "Użytkownik",
|
||||
"subscription": "Typ subskrypcji",
|
||||
"user": "User",
|
||||
"subscription": "Subscription Type",
|
||||
"last-delivery": "Last successful delivery",
|
||||
"default": "Domyślne ustawienie systemowe",
|
||||
"default": "System default",
|
||||
"default-help": "<em>System default</em> means the user has not explicitly overridden the global forum setting for digests, which is currently: "<strong>%1</strong>"",
|
||||
"resend": "Wyślij ponownie podsumowanie",
|
||||
"resend-all-confirm": "Czy na pewno chcesz ręcznie wykonać włącznie podsumowania?",
|
||||
"resent-single": "Ręczne wysyłanie podsumowania zakończone",
|
||||
"resent-day": "Codzienne podsumowanie",
|
||||
"resent-week": "Tygodniowe podsumowanie",
|
||||
"resent-month": "Miesięczne podsumowanie",
|
||||
"null": "<em>Nigdy</em>",
|
||||
"manual-run": "Włącz ręcznie podsumowania",
|
||||
"resend": "Resend Digest",
|
||||
"resend-all-confirm": "Are you sure you wish to mnually execute this digest run?",
|
||||
"resent-single": "Manual digest resend completed",
|
||||
"resent-day": "Daily digest resent",
|
||||
"resent-week": "Weekly digest resent",
|
||||
"resent-month": "Monthly digest resent",
|
||||
"null": "<em>Never</em>",
|
||||
"manual-run": "Manual digest run:",
|
||||
|
||||
"no-delivery-data": "Nie znaleziono danych"
|
||||
"no-delivery-data": "No delivery data found"
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"global": "Globalny",
|
||||
"global.no-users": "Brak globalnych uprawnień zdefiniowanych dla użytkownika",
|
||||
"group-privileges": "Uprawnienia grup",
|
||||
"user-privileges": "Uprawnienia użytkownika",
|
||||
"group-privileges": "Group Privileges",
|
||||
"user-privileges": "User Privileges",
|
||||
"chat": "Dostęp do czatu",
|
||||
"upload-images": "Przesyłanie zdjęć",
|
||||
"upload-files": "Przesyłanie plików",
|
||||
@@ -16,7 +16,7 @@
|
||||
"view-groups": "Wyświetlanie grup",
|
||||
"allow-local-login": "Logowanie lokalne",
|
||||
"allow-group-creation": "Tworzenie grup",
|
||||
"view-users-info": "Pokaż dane użytkownika",
|
||||
"view-users-info": "View Users Info",
|
||||
"find-category": "Szukanie kategorii",
|
||||
"access-category": "Dostęp do kategorii",
|
||||
"access-topics": "Dostęp do tematów",
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
"delete": "Usuń użytkownika(-ów)",
|
||||
"purge": "Usuń użytkownika(-ów) oraz zawartość",
|
||||
"download-csv": "Pobierz CSV",
|
||||
"manage-groups": "Zarządzaj grupami",
|
||||
"add-group": "Dodaj grupę",
|
||||
"manage-groups": "Manage Groups",
|
||||
"add-group": "Add Group",
|
||||
"invite": "Zaproś",
|
||||
"new": "Nowy użytkownik",
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"manage/groups": "Grupy",
|
||||
"manage/ip-blacklist": "Czarna lista IP",
|
||||
"manage/uploads": "Przesłane pliki",
|
||||
"manage/digest": "Podsumownia",
|
||||
"manage/digest": "Digests",
|
||||
|
||||
"section-settings": "Ustawienia",
|
||||
"settings/general": "Ogólne",
|
||||
|
||||
@@ -33,8 +33,8 @@
|
||||
"testing.select": "Wybierz szablon e-maila",
|
||||
"testing.send": "Wyślij testowy e-mail",
|
||||
"testing.send-help": "Testowy e-mail zostanie wysłany na adres aktualnie zalogowanego użytkownika.",
|
||||
"subscriptions": "Podsumowania e-mail",
|
||||
"subscriptions.disable": "Wyłącz podsumowania e-maili",
|
||||
"subscriptions": "Email Digests",
|
||||
"subscriptions.disable": "Disable email digests",
|
||||
"subscriptions.hour": "Godzina podsumowania",
|
||||
"subscriptions.hour-help": "Wprowadź liczbę odpowiadającą godzinie, o której mają być wysyłane regularne e-maile z podsumowaniem (np. <code>0</code> dla północy lub <code>17</code> dla 17:00). Pamiętaj, że godzina jest godziną serwera i nie musi zgadzać się z czasem lokalnym administratora. Przybliżony czas serwera to: <span id=\"serverTime\"></span><br /> Wysłanie kolejnego e-maila z podsumowaniem zaplanowano na <span id=\"nextDigestTime\"></span>"
|
||||
}
|
||||
@@ -7,12 +7,12 @@
|
||||
"sorting.most-posts": "Najwięcej postów",
|
||||
"sorting.topic-default": "Domyślne sortowanie tematów",
|
||||
"length": "Długość postu",
|
||||
"post-queue": "Kolejka postów",
|
||||
"post-queue": "Post Queue",
|
||||
"restrictions": "Restrykcje postowania",
|
||||
"restrictions-new": "Restrykcje dla nowych użytkowników",
|
||||
"restrictions.post-queue": "Włącz kolejkę postów",
|
||||
"restrictions.post-queue-rep-threshold": "Reputacja wymagana do ominięcia kolejki postów",
|
||||
"restrictions.groups-exempt-from-post-queue": "Wybierz grupy, które powinny być zwolnione z kolejki postów",
|
||||
"restrictions.post-queue-rep-threshold": "Reputation required to bypass post queue",
|
||||
"restrictions.groups-exempt-from-post-queue": "Select groups that should be exempt from the post queue",
|
||||
"restrictions-new.post-queue": "Włącz restrykcje dla nowych użytkowników",
|
||||
"restrictions.post-queue-help": "Włączenie kolejki postów spowoduje umieszczenie postów nowych użytkowników w kolejce do zatwierdzenia",
|
||||
"restrictions-new.post-queue-help": "Włączenie restrykcji dla nowych użytkowników ustawi restrykcje na ich wpisy.",
|
||||
@@ -32,8 +32,8 @@
|
||||
"timestamp": "Znacznik czasowy",
|
||||
"timestamp.cut-off": "Termin odcięcia (w dniach)",
|
||||
"timestamp.cut-off-help": "Daty oraz godziny będą wyświetlane w sposób relatywny (np. \"3 godziny temu\" / \"5 dni temu\"), oraz przetłumaczone na różne\n\t\t\t\t\tjęzyki. Po określonym czasie, ten tekst może zostać zmieniony, aby wyświetlać sformatowane daty.\n\t\t\t\t\t(np. 4 Lut 2017 12:45).<br /><em>(domyślnie: <code>30</code>, lub jeden miesiąc). Ustaw 0, aby zawsze wyświetlać daty; pozostaw puste, aby korzystać z tylko z relatywnych opisów.</em>",
|
||||
"timestamp.necro-threshold": "Próg nekro (w dniach)",
|
||||
"timestamp.necro-threshold-help": "Komunikat zostanie wyświetlony między postami, jeśli czas między nimi jest dłuższy niż próg nekro. (Domyślnie: <code>7</code> lub jeden tydzień). Ustaw na 0, aby wyłączyć.</em>",
|
||||
"timestamp.necro-threshold": "Necro Threshold (in days)",
|
||||
"timestamp.necro-threshold-help": "A message will be shown between posts if the time between them is longer than the necro threshold. (Default: <code>7</code>, or one week). Set to 0 to disable.</em>",
|
||||
"teaser": "Zwiastun postu",
|
||||
"teaser.last-post": "Ostatni – Pokaż ostatni post, włączając pierwszy post, w razie braku odpowiedzi",
|
||||
"teaser.last-reply": "Ostatni – Pokaż ostatnią odpowiedź lub komunikat „Brak odpowiedzi” w razie ich braku",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"notif.chat.cta": "Kliknij tutaj, aby kontynuować rozmowę",
|
||||
"notif.chat.unsub.info": "To powiadomienie o czacie zostało wysłane zgodnie z Twoimi ustawieniami.",
|
||||
"notif.post.unsub.info": "To powiadomienie o poście zostało wysłane zgodnie z Twoimi ustawieniami.",
|
||||
"notif.post.unsub.one-click": "Możesz zrezygnować z otrzymywania takich e-maili w przyszłości, klikając",
|
||||
"notif.post.unsub.one-click": "Alternatively, unsubscribe from future emails like this, by clicking",
|
||||
"notif.cta": "Na forum",
|
||||
"notif.cta-new-reply": "Pokaż wpisy",
|
||||
"notif.cta-new-chat": "Pokaż czat",
|
||||
@@ -42,8 +42,8 @@
|
||||
"notif.test.long": "To jest email testowy z powiadomieniami. Wyślij pomoc!",
|
||||
"test.text1": "To jest e-mail testowy wysyłany w celu sprawdzenia konfiguracji e-mailera w NodeBB.",
|
||||
"unsub.cta": "Kliknij tutaj, aby zmienić te ustawienia",
|
||||
"unsubscribe": "Wypisz się",
|
||||
"unsub.success": "Nie będziesz już otrzymywać wiadomości e-mail z <strong>%1</strong>",
|
||||
"unsubscribe": "unsubscribe",
|
||||
"unsub.success": "You will no longer receive emails from the <strong>%1</strong> mailing list",
|
||||
"banned.subject": "Zostałeś zbanowany na %1",
|
||||
"banned.text1": "Użytkownik %1 został zbanowany na %2.",
|
||||
"banned.text2": "Ban potrwa do %1",
|
||||
|
||||
@@ -5,15 +5,15 @@
|
||||
"account-locked": "Twoje konto zostało tymczasowo zablokowane",
|
||||
"search-requires-login": "Wyszukiwanie wymaga konta - zaloguj się lub zarejestruj.",
|
||||
"goback": "Wciśnij wstecz, aby powrócić do poprzedniej strony",
|
||||
"invalid-cid": "Nieprawidłowy ID kategorii",
|
||||
"invalid-tid": "Nieprawidłowy ID tematu",
|
||||
"invalid-pid": "Nieprawidłowy ID posta",
|
||||
"invalid-uid": "Nieprawidłowy ID użytkownika",
|
||||
"invalid-username": "Nieprawidłowy login",
|
||||
"invalid-email": "Nieprawidłowy adres e-mail",
|
||||
"invalid-fullname": "Nieprawidłowa nazwa",
|
||||
"invalid-location": "Nieprawidłowa lokalizacja",
|
||||
"invalid-birthday": "Nieprawidłowa data urodzenia",
|
||||
"invalid-cid": "Błędne ID kategorii",
|
||||
"invalid-tid": "Błędne ID tematu",
|
||||
"invalid-pid": "Błędne ID posta",
|
||||
"invalid-uid": "Błędne ID użytkownika",
|
||||
"invalid-username": "Błędny login",
|
||||
"invalid-email": "Błędny e-mail",
|
||||
"invalid-fullname": "Invalid Fullname",
|
||||
"invalid-location": "Invalid Location",
|
||||
"invalid-birthday": "Invalid Birthday",
|
||||
"invalid-title": "Błędna nazwa",
|
||||
"invalid-user-data": "Błędne dane użytkownika",
|
||||
"invalid-password": "Błędne hasło",
|
||||
@@ -26,14 +26,14 @@
|
||||
"invalid-pagination-value": "Błędna wartość paginacji, zakres od %1 do %2",
|
||||
"username-taken": "Login zajęty",
|
||||
"email-taken": "Email zajęty",
|
||||
"email-not-confirmed": "Nie możesz opublikować, dopóki Twój adres e-mail nie zostanie potwierdzony, kliknij tutaj, aby potwierdzić swój adres e-mail.",
|
||||
"email-not-confirmed": "You are unable to post until your email is confirmed, please click here to confirm your email.",
|
||||
"email-not-confirmed-chat": "Nie możesz prowadzić rozmów, dopóki twój email nie zostanie potwierdzony. Kliknij tutaj, aby potwierdzić swój email.",
|
||||
"email-not-confirmed-email-sent": "Twój e-mail nie został jeszcze potwierdzony, sprawdź swoją skrzynkę pocztową, aby znaleźć e-mail z potwierdzeniem. Nie będziesz mógł dodawać postów ani czatować, dopóki Twój adres e-mail nie zostanie potwierdzony.",
|
||||
"email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.",
|
||||
"no-email-to-confirm": "To forum wymaga weryfikacji przez email. Proszę kliknąć tutaj, aby wprowadzić adres.",
|
||||
"email-confirm-failed": "Nie byliśmy w stanie potwierdzić Twojego adresu e-mail. Spróbuj później.",
|
||||
"confirm-email-already-sent": "Email potwierdzający został już wysłany, proszę odczekaj jeszcze %1 minut(y), aby wysłać kolejny.",
|
||||
"sendmail-not-found": "Program sendmail nie został znaleziony, proszę upewnij się, że jest zainstalowany i możliwy do uruchomienia przez użytkownika uruchamiającego NodeBB.",
|
||||
"digest-not-enabled": "Ten użytkownik nie ma włączonych skrótów lub system nie jest skonfigurowany do wysyłania skrótów",
|
||||
"digest-not-enabled": "This user does not have digests enabled, or the system default is not configured to send digests",
|
||||
"username-too-short": "Nazwa użytkownika za krótka",
|
||||
"username-too-long": "Zbyt długa nazwa użytkownika",
|
||||
"password-too-long": "Hasło jest za długie",
|
||||
@@ -103,8 +103,8 @@
|
||||
"group-needs-owner": "Ta grupa musi mieć przynajmniej jednego właściciela",
|
||||
"group-already-invited": "Ten użytkownik został już zaproszony",
|
||||
"group-already-requested": "Twoje podanie o członkostwo zostało już wysłane",
|
||||
"group-join-disabled": "Nie możesz teraz dołączyć do tej grupy",
|
||||
"group-leave-disabled": "Obecnie nie możesz opuścić tej grupy",
|
||||
"group-join-disabled": "You are not able to join this group at this time",
|
||||
"group-leave-disabled": "You are not able to leave this group at this time",
|
||||
"post-already-deleted": "Ten post został już skasowany",
|
||||
"post-already-restored": "Ten post został już przywrócony",
|
||||
"topic-already-deleted": "Ten temat został już skasowany",
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"details.latest_posts": "Ostatnie posty",
|
||||
"details.private": "Prywatna",
|
||||
"details.disableJoinRequests": "Wyłączono prośbę o dołączenie",
|
||||
"details.disableLeave": "Wyłącz możliwość opuszczania użytkowników z grupy",
|
||||
"details.disableLeave": "Disallow users from leaving the group",
|
||||
"details.grant": "Nadaj/Cofnij prawa Właściciela",
|
||||
"details.kick": "Wykop",
|
||||
"details.kick_confirm": "Jesteś pewny, że chcesz wyrzucić tego użytkownika z grupy?",
|
||||
@@ -49,11 +49,11 @@
|
||||
"event.updated": "Dane grupy zostały zaktualizowane",
|
||||
"event.deleted": "Grupa \"%1\" została usunięta",
|
||||
"membership.accept-invitation": "Przyjmij zaproszenie",
|
||||
"membership.accept.notification_title": "Jesteś teraz członkiem <strong>%1</strong>",
|
||||
"membership.accept.notification_title": "You are now a member of <strong>%1</strong>",
|
||||
"membership.invitation-pending": "Oczekujące zaproszenie",
|
||||
"membership.join-group": "Dołącz do grupy",
|
||||
"membership.leave-group": "Opuść grupę",
|
||||
"membership.leave.notification_title": "<strong>%1</strong> opuścił grupę <strong>%2</strong>",
|
||||
"membership.leave.notification_title": "<strong>%1</strong> has left group <strong>%2</strong>",
|
||||
"membership.reject": "Odrzuć",
|
||||
"new-group.group_name": "Nazwa grupy:",
|
||||
"upload-group-cover": "Prześlij zdjęcie tła grupy",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"reset_password": "Zresetuj hasło",
|
||||
"update_password": "Zaktualizuj hasło",
|
||||
"update_password": "Zmień hasło",
|
||||
"password_changed.title": "Hasło zmienione",
|
||||
"password_changed.message": "<p>Hasło zostało zmienione. <a href=\"/login\">Zaloguj się ponownie</a>.",
|
||||
"wrong_reset_code.title": "Nieprawidłowy kod resetujący",
|
||||
@@ -9,7 +9,7 @@
|
||||
"repeat_password": "Powtórz hasło",
|
||||
"enter_email": "Podaj swój <strong>adres e-mail</strong>, by otrzymać wiadomość z instrukcjami, jak zresetować hasło.",
|
||||
"enter_email_address": "Wpisz swój adres e-mail",
|
||||
"password_reset_sent": "Jeśli podany adres odpowiada istniejącemu kontu użytkownika, to zostanie wysłana wiadomość e-mail dotyczącą resetowania hasła. Pamiętaj, że na minutę zostanie wysłany tylko jeden e-mail.",
|
||||
"password_reset_sent": "If the specified address corresponds to an existing user account, a password reset email was sent. Please note that only one email will be sent per minute.",
|
||||
"invalid_email": "Nieprawidłowy adres e-mail.",
|
||||
"password_too_short": "Wprowadzone hasło jest zbyt krótkie, wybierz inne hasło.",
|
||||
"passwords_do_not_match": "Wprowadzone hasła nie pasują do siebie",
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
"moved": "Przeniesiony",
|
||||
"copy-ip": "Kopiuj IP",
|
||||
"ban-ip": "Blokuj IP",
|
||||
"view-history": "Historia edycji",
|
||||
"view-history": "Edytuj historię",
|
||||
"bookmark_instructions": "Kliknij tutaj, by powrócić do ostatniego przeczytanego postu w tym temacie.",
|
||||
"flag_title": "Zgłoś post do moderacji",
|
||||
"merged_message": "Ten temat został połączony z <a href=\"/topic/%1\">%2</a>",
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
"reputation": "Reputacja",
|
||||
"bookmarks": "Zakładki",
|
||||
"watched_categories": "Obserwowane kategorie",
|
||||
"change_all": "Zmień wszystko",
|
||||
"change_all": "Change All",
|
||||
"watched": "Obserwowane",
|
||||
"ignored": "Zignorowane",
|
||||
"default-category-watch-state": "Domyślny stan oglądania kategorii",
|
||||
@@ -125,7 +125,7 @@
|
||||
"follow_topics_you_reply_to": "Obserwuj tematy, w których uczestniczysz",
|
||||
"follow_topics_you_create": "Obserwuj tematy, które utworzyłeś",
|
||||
"grouptitle": "Nazwa grupy",
|
||||
"group-order-help": "Wybierz grupę i użyj strzałek, aby zamówić tytuł",
|
||||
"group-order-help": "Select a group and use the arrows to order titles",
|
||||
"no-group-title": "Brak nazwy grupy",
|
||||
"select-skin": "Wybierz skórkę",
|
||||
"select-homepage": "Wybierz stronę startową",
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
"plugin-item.install": "Установить",
|
||||
"plugin-item.uninstall": "Удалить",
|
||||
"plugin-item.settings": "Настройки",
|
||||
"plugin-item.installed": "Установленная версия",
|
||||
"plugin-item.latest": "Последняя версия",
|
||||
"plugin-item.installed": "Установленные",
|
||||
"plugin-item.latest": "Недавние",
|
||||
"plugin-item.upgrade": "Обновить",
|
||||
"plugin-item.more-info": "Дополнительная информация:",
|
||||
"plugin-item.unknown": "Неизвестно",
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
"watch": "Стежити",
|
||||
"ignore": "Ігнорувати",
|
||||
"watching": "Відстежується",
|
||||
"not-watching": "Не спостерігається",
|
||||
"not-watching": "Not Watching",
|
||||
"ignoring": "Ігнорувати",
|
||||
"watching.description": "Показати теми в непрочитаних та останніх",
|
||||
"not-watching.description": "Не показувати теми в непрочитаних, показувати в останніх",
|
||||
"ignoring.description": "Не показувати теми в непрочитаних і останніх",
|
||||
"watching.message": "Ви зараз спостерігаєте за оновленнями з цієї категорії та всіх її підкатегорій",
|
||||
"notwatching.message": "Зараз ви не спостерігаєте за оновленнями з цієї категорії та всіх її підкатегорій",
|
||||
"ignoring.message": "Зараз ви ігноруєте оновлення з цієї категорії та всіх її підкатегорій",
|
||||
"watching.description": "Show topics in unread and recent",
|
||||
"not-watching.description": "Do not show topics in unread, show in recent",
|
||||
"ignoring.description": "Do not show topics in unread and recent",
|
||||
"watching.message": "You are now watching updates from this category and all subcategories",
|
||||
"notwatching.message": "You are not watching updates from this category and all subcategories",
|
||||
"ignoring.message": "You are now ignoring updates from this category and all subcategories",
|
||||
"watched-categories": "Переглянуті категорії"
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
{
|
||||
"test-email.subject": "Тестове поштове повідомлення",
|
||||
"password-reset-requested": "Отримано запит на скидання пароля!",
|
||||
"test-email.subject": "Test Email",
|
||||
"password-reset-requested": "Password Reset Requested!",
|
||||
"welcome-to": "Ласкаво просимо до %1",
|
||||
"invite": "Запрошення від %1",
|
||||
"greeting_no_name": "Привіт",
|
||||
"greeting_with_name": "Привіт %1",
|
||||
"email.verify-your-email.subject": "Будь-ласка перевірте вашу електронну адресу",
|
||||
"email.verify.text1": "Ваша електронна адреса змінилась!",
|
||||
"email.verify-your-email.subject": "Please verify your email",
|
||||
"email.verify.text1": "Your email address has changed!",
|
||||
"welcome.text1": "Дякуємо за реєстрацію з %1!",
|
||||
"welcome.text2": "Щоб повністю активувати ваш акаунт, нам потрібно перевірити, що вам належить електронна адреса, яку ви вказали при реєстрації ",
|
||||
"welcome.text3": "Адміністратор схвалив ваш запит на реєстрацію. Ви можете залогінитись, використовуючи свій пароль та назву акаунту",
|
||||
"welcome.cta": "Натисніть тут, щоб підтвердити вашу електронну адресу",
|
||||
"invitation.text1": "%1 запросив вас приєднатися до %2",
|
||||
"invitation.text2": "Термін дії вашого запрошення закінчиться за %1 днів.",
|
||||
"invitation.cta": "Натисніть тут щоб створити акаунт.",
|
||||
"invitation.cta": "Click here to create your account.",
|
||||
"reset.text1": "Ми отримали запит на відновлення вашого паролю, можливо тому, что ви його забули. Якщо вам це не потрібно - проігноруйте цей лист",
|
||||
"reset.text2": "Щоб продовжити відновлення паролю, будь ласка, перейдіть за посиланням",
|
||||
"reset.cta": "Натисніть тут щоб скинути Ваш пароль",
|
||||
@@ -27,23 +27,23 @@
|
||||
"digest.week": "тиждень",
|
||||
"digest.month": "місяць",
|
||||
"digest.subject": "Дайджест для %1",
|
||||
"digest.title.day": "Ваш щоденний дайджест",
|
||||
"digest.title.week": "Ваш тижневий дайджест",
|
||||
"digest.title.month": "Ваш місячний дайджест",
|
||||
"digest.title.day": "Your Daily Digest",
|
||||
"digest.title.week": "Your Weekly Digest",
|
||||
"digest.title.month": "Your Monthly Digest",
|
||||
"notif.chat.subject": "Отримане нове повідомлення чату від %1",
|
||||
"notif.chat.cta": "Натисніть тут, щоб продовжити розмову",
|
||||
"notif.chat.unsub.info": "Це повідомлення чату було вислано вам, згідно ваших налаштувань підписки",
|
||||
"notif.post.unsub.info": "Це поштове повідомлення було вислано вам, згідно ваших налаштувань підписки",
|
||||
"notif.post.unsub.one-click": "Ви також можете відписатись від схожих майбутніх повідомлень, натиснувши тут",
|
||||
"notif.cta": "На форум",
|
||||
"notif.cta-new-reply": "Переглянути допис",
|
||||
"notif.cta-new-chat": "Переглянути чат",
|
||||
"notif.test.short": "Перевірка сповіщень",
|
||||
"notif.test.long": "Це перевірка повідомлення про сповіщення.",
|
||||
"notif.post.unsub.one-click": "Alternatively, unsubscribe from future emails like this, by clicking",
|
||||
"notif.cta": "To the forum",
|
||||
"notif.cta-new-reply": "View Post",
|
||||
"notif.cta-new-chat": "View Chat",
|
||||
"notif.test.short": "Testing Notifications",
|
||||
"notif.test.long": "This is a test of the notifications email. Send help!",
|
||||
"test.text1": "Це пробний лист для верифікації поштової служби. Всі налаштування вірні для NodeBB.",
|
||||
"unsub.cta": "Натисніть тут, щоб змінити ці налаштування",
|
||||
"unsubscribe": "відписатись",
|
||||
"unsub.success": "Ви більше не будете отримувати повідомлення з <strong>%1</strong> поштової розсилки",
|
||||
"unsubscribe": "unsubscribe",
|
||||
"unsub.success": "You will no longer receive emails from the <strong>%1</strong> mailing list",
|
||||
"banned.subject": "Ви були забанені на %1",
|
||||
"banned.text1": "Користувач %1 був забанений на %2.",
|
||||
"banned.text2": "Тривалість бану - до %1.",
|
||||
|
||||
@@ -11,9 +11,9 @@
|
||||
"invalid-uid": "Невірний ID користувача",
|
||||
"invalid-username": "Невірне ім'я користувача",
|
||||
"invalid-email": "Невірна електронна адреса",
|
||||
"invalid-fullname": "Невірне повне ім'я",
|
||||
"invalid-location": "Невірне місцезнаходження",
|
||||
"invalid-birthday": "Невірна дата народження",
|
||||
"invalid-fullname": "Invalid Fullname",
|
||||
"invalid-location": "Invalid Location",
|
||||
"invalid-birthday": "Invalid Birthday",
|
||||
"invalid-title": "Невірний заголовок",
|
||||
"invalid-user-data": "Невірні користувацькі дані",
|
||||
"invalid-password": "Невірний пароль",
|
||||
@@ -26,18 +26,18 @@
|
||||
"invalid-pagination-value": "Невірне значення сторінки, має бути щонайменше %1 та щонайбільше %2",
|
||||
"username-taken": "Це ім'я зайняте",
|
||||
"email-taken": "Ця електронна пошта зайнята",
|
||||
"email-not-confirmed": "Ви не зможете постити до підтвердження вашої електронної адреси, будь-ласка натисніть тут щоб підтвердити свій емейл.",
|
||||
"email-not-confirmed": "You are unable to post until your email is confirmed, please click here to confirm your email.",
|
||||
"email-not-confirmed-chat": "Ви не можете користуватися чатом поки ваша електронна пошта не буде підтверджена, натисніть тут, щоб це зробити.",
|
||||
"email-not-confirmed-email-sent": "Ваша електронна адреса ще не була підтверджена, будь-ласка перевірте свою поштову скриньку. Ви не зможете постити або чатитись до того як ваш емейл підтверджено.",
|
||||
"email-not-confirmed-email-sent": "Your email has not been confirmed yet, please check your inbox for the confirmation email. You won't be able to post or chat until your email is confirmed.",
|
||||
"no-email-to-confirm": "Цей форум вимагає підтвердження електронної пошти, будь-ласка, натисніть тут, щоб його ввести.",
|
||||
"email-confirm-failed": "Ми не можемо підтвердити вашу електронну пошту, будь ласка, спробуйте пізніше.",
|
||||
"confirm-email-already-sent": "Підтвердження по електронній пошті вже було надіслано, зачекайте, будь ласка, %1 хвилин(и), щоб відправити ще одне. ",
|
||||
"sendmail-not-found": "Виконуваний файл sendmail не знайдено, переконайтесь, будь ласка, що його встановлено та що він виконується власником процесу NodeBB.",
|
||||
"digest-not-enabled": "Цей користувач не має активних дайджестів, або налаштування по замовчанню не включають надсилання дайджестів.",
|
||||
"digest-not-enabled": "This user does not have digests enabled, or the system default is not configured to send digests",
|
||||
"username-too-short": "Ім'я користувача закоротке",
|
||||
"username-too-long": "Ім'я користувача задовге",
|
||||
"password-too-long": "Пароль задовгий",
|
||||
"reset-rate-limited": "Занадто багато запитів на скидання паролю (кількість за період часу обмежена)",
|
||||
"reset-rate-limited": "Too many password reset requests (rate limited)",
|
||||
"user-banned": "Користувача забанено",
|
||||
"user-banned-reason": "Вибачте, але цей акаунт було забанено (Причина: %1)",
|
||||
"user-banned-reason-until": "Вибачте, цей акаунт забанений до %1 (Причина: %2)",
|
||||
@@ -83,7 +83,7 @@
|
||||
"still-uploading": "Зачекайте, будь ласка, доки завантаження завершиться.",
|
||||
"file-too-big": "Максимальний розмір файлу %1 кБ — завантажте менший файл, будь ласка.",
|
||||
"guest-upload-disabled": "Гостьове завантаження вимкнено.",
|
||||
"cors-error": "Неможливо завантажити зображення через неправильно налаштований CORS",
|
||||
"cors-error": "Unable to upload image due to misconfigured CORS",
|
||||
"already-bookmarked": "Ви вже додали цей пост собі в закладки",
|
||||
"already-unbookmarked": "Ви вже видалили цей пост із закладок",
|
||||
"cant-ban-other-admins": "Ви не можете банити інших адмінів!",
|
||||
@@ -93,7 +93,7 @@
|
||||
"invalid-image-type": "Невірний тип зображення. Дозволені типи: %1",
|
||||
"invalid-image-extension": "Невірне розширення зображення",
|
||||
"invalid-file-type": "Невірний тип файлу. Дозволені типи: %1",
|
||||
"invalid-image-dimensions": "Зображення занадто велике",
|
||||
"invalid-image-dimensions": "Image dimensions are too big",
|
||||
"group-name-too-short": "Ім'я групи занадто коротке",
|
||||
"group-name-too-long": "Ім'я групи занадто довге",
|
||||
"group-already-exists": "Група вже існує",
|
||||
@@ -103,8 +103,8 @@
|
||||
"group-needs-owner": "Ця група потребує щонайменше одного власника",
|
||||
"group-already-invited": "Користувача вже було запрошено",
|
||||
"group-already-requested": "Ваша заявка на вступ вже подана",
|
||||
"group-join-disabled": "Ви не можете приєднатись до цієї групи зараз",
|
||||
"group-leave-disabled": "Ви не можете покинути цю групу зараз",
|
||||
"group-join-disabled": "You are not able to join this group at this time",
|
||||
"group-leave-disabled": "You are not able to leave this group at this time",
|
||||
"post-already-deleted": "Цей пост вже видалено",
|
||||
"post-already-restored": "Цей пост вже відновлено",
|
||||
"topic-already-deleted": "Ця тема вже була видалена",
|
||||
@@ -127,7 +127,7 @@
|
||||
"chat-edit-duration-expired": "Ви можете редагувати повідомлення чату лише через %1 секунд після публікації",
|
||||
"chat-delete-duration-expired": "Ви можете видаляти повідомлення чату лише через %1 секунд після публікації",
|
||||
"chat-deleted-already": "Це повідомлення чату вже було видалено.",
|
||||
"chat-restored-already": "Це чат повідомлення вже було відновлене",
|
||||
"chat-restored-already": "This chat message has already been restored.",
|
||||
"already-voting-for-this-post": "Ви вже проголосували за цей пост.",
|
||||
"reputation-system-disabled": "Система репутацій вимкнена.",
|
||||
"downvoting-disabled": "Голосування проти вимкнено",
|
||||
@@ -159,8 +159,8 @@
|
||||
"cant-move-to-same-topic": "Ви не можете перемістити пост до тієї ж самої теми!",
|
||||
"cannot-block-self": "Ви не можете заблокувати самого себе!",
|
||||
"cannot-block-privileged": "Ви не можете заблокувати адміністраторів або глобальних модераторів",
|
||||
"cannot-block-guest": "Гості не можуть блокувати інших користувачів",
|
||||
"already-blocked": "Цей користувач вже заблокований",
|
||||
"already-unblocked": "Цей користувач вже розблокований",
|
||||
"cannot-block-guest": "Guest are not able to block other users",
|
||||
"already-blocked": "This user is already blocked",
|
||||
"already-unblocked": "This user is already unblocked",
|
||||
"no-connection": "Схоже, виникла проблема з вашим Інтернет-з'єднанням"
|
||||
}
|
||||
@@ -59,8 +59,8 @@
|
||||
"downvoted": "Проти",
|
||||
"views": "Перегляди",
|
||||
"reputation": "Репутація",
|
||||
"lastpost": "Останній допис",
|
||||
"firstpost": "Перший допис",
|
||||
"lastpost": "Last post",
|
||||
"firstpost": "First post",
|
||||
"read_more": "читати далі",
|
||||
"more": "Більше",
|
||||
"posted_ago_by_guest": "запостив Гість %1",
|
||||
@@ -87,7 +87,7 @@
|
||||
"language": "Мова",
|
||||
"guest": "Гість",
|
||||
"guests": "Гості",
|
||||
"former_user": "Колишній користувач",
|
||||
"former_user": "A Former User",
|
||||
"updated.title": "Форум оновлено",
|
||||
"updated.message": "Форум було щойно оновлено до останньої версії. Клікніть тут, щоб оновити сторінку.",
|
||||
"privacy": "Приватність",
|
||||
|
||||
@@ -25,11 +25,11 @@
|
||||
"details.latest_posts": "Останні пости",
|
||||
"details.private": "Приватна",
|
||||
"details.disableJoinRequests": "Вимкнути запити на приєднання",
|
||||
"details.disableLeave": "Забороніть користувачам покидати групу",
|
||||
"details.disableLeave": "Disallow users from leaving the group",
|
||||
"details.grant": "Надати/забрати права адміністратора",
|
||||
"details.kick": "Вигнати",
|
||||
"details.kick_confirm": "Ви впевнені, що бажаєте видалити цього користувача з групи?",
|
||||
"details.add-member": "Додати члена групи",
|
||||
"details.add-member": "Add Member",
|
||||
"details.owner_options": "Адміністрація групи",
|
||||
"details.group_name": "Назва групи",
|
||||
"details.member_count": "Кількість учасників",
|
||||
@@ -37,8 +37,8 @@
|
||||
"details.description": "Опис",
|
||||
"details.badge_preview": "Попередній перегляд бейджа",
|
||||
"details.change_icon": "Змінити іконку",
|
||||
"details.change_label_colour": "Змінити колір позначки",
|
||||
"details.change_text_colour": "Змінити колір тексту",
|
||||
"details.change_label_colour": "Change Label Colour",
|
||||
"details.change_text_colour": "Change Text Colour",
|
||||
"details.badge_text": "Текст бейджа",
|
||||
"details.userTitleEnabled": "Показати бейдж",
|
||||
"details.private_help": "Якщо увімкнено, приєднання до групи вимагає підтвердження власника.",
|
||||
@@ -49,11 +49,11 @@
|
||||
"event.updated": "Деталі групи оновлено",
|
||||
"event.deleted": "Група \"%1\" видалена",
|
||||
"membership.accept-invitation": "Прийняти запрошення",
|
||||
"membership.accept.notification_title": "Тепер ви є членом <strong>%1</strong>",
|
||||
"membership.accept.notification_title": "You are now a member of <strong>%1</strong>",
|
||||
"membership.invitation-pending": "Запрошення в черзі",
|
||||
"membership.join-group": "Приєднатися до групи",
|
||||
"membership.leave-group": "Покинути групу",
|
||||
"membership.leave.notification_title": "<strong>%1</strong> покинув групу <strong>%2</strong>",
|
||||
"membership.leave.notification_title": "<strong>%1</strong> has left group <strong>%2</strong>",
|
||||
"membership.reject": "Відхилити",
|
||||
"new-group.group_name": "Назва групи:",
|
||||
"upload-group-cover": "Завантажити обкладинку групи",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"chat.delete_message_confirm": "Ви впевнені, що хочете видалити це повідомлення?",
|
||||
"chat.retrieving-users": "Отримання користувачів...",
|
||||
"chat.manage-room": "Управління чат кімнатами",
|
||||
"chat.add-user-help": "Шукайте користувачів тут. Користувача можна додати до чату, обравши його. Нові користувачі не можуть бачити повідомлення, написані до того, як їх додали до розмови. Тільки власники кімнат можуть видаляти користувачів з кімнат.",
|
||||
"chat.add-user-help": "Search for users here. When selected, the user will be added to the chat. The new user will not be able to see chat messages written before they were added to the conversation. Only room owners (<i class=\"fa fa-star text-warning\"></i>) may remove users from chat rooms.",
|
||||
"chat.confirm-chat-with-dnd-user": "Користувач змінив свій статус на DnD (Не турбувати). Ви дійсно бажаєте надіслати йому повідомлення в чат?",
|
||||
"chat.rename-room": "Перейменувати Кімнату",
|
||||
"chat.rename-placeholder": "Введіть назву своєї кімнати тут",
|
||||
@@ -33,10 +33,10 @@
|
||||
"chat.in-room": "У цій кімнаті",
|
||||
"chat.kick": "Штурхнути",
|
||||
"chat.show-ip": "Показати IP",
|
||||
"chat.owner": "Власник кімнати",
|
||||
"chat.system.user-join": "%1 зайшов в кімнату",
|
||||
"chat.system.user-leave": "%1 покинув кімнату",
|
||||
"chat.system.room-rename": "%2 перейменував кімнату на: %1",
|
||||
"chat.owner": "Room Owner",
|
||||
"chat.system.user-join": "%1 has joined the room",
|
||||
"chat.system.user-leave": "%1 has left the room",
|
||||
"chat.system.room-rename": "%2 has renamed this room: %1",
|
||||
"composer.compose": "Редактор повідомлень",
|
||||
"composer.show_preview": "Показати попередній перегляд",
|
||||
"composer.hide_preview": "Сховати попередній перегляд",
|
||||
@@ -50,7 +50,7 @@
|
||||
"composer.formatting.italic": "Курсив",
|
||||
"composer.formatting.list": "Список",
|
||||
"composer.formatting.strikethrough": "Закреслений",
|
||||
"composer.formatting.code": "Код",
|
||||
"composer.formatting.code": "Code",
|
||||
"composer.formatting.link": "Посилання",
|
||||
"composer.formatting.picture": "Зображення",
|
||||
"composer.upload-picture": "Завантажити зображення",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"outgoing_link_message": "Ви залишаєте %1",
|
||||
"continue_to": "Перейти до %1",
|
||||
"return_to": "Повернутись до %1",
|
||||
"new_notification": "У вас нове сповіщення",
|
||||
"new_notification": "You have a new notification",
|
||||
"you_have_unread_notifications": "У вас немає непрочитаних сповіщень",
|
||||
"all": "Всі",
|
||||
"topics": "Теми",
|
||||
@@ -56,7 +56,7 @@
|
||||
"notificationType_follow": "Коли хтось починає слідкувати за вами",
|
||||
"notificationType_new-chat": "Коли ви отримуєте повідомлення чату",
|
||||
"notificationType_group-invite": "Коли ви отримуєте запрошення до групи",
|
||||
"notificationType_group-request-membership": "Коли хтось подає запит на приєднання до групи, якою ви володієте",
|
||||
"notificationType_group-request-membership": "When someone requests to join a group you own",
|
||||
"notificationType_new-register": "Коли когось додано до черги на реєстрацію",
|
||||
"notificationType_post-queue": "Коли новий пост знаходиться в черзі",
|
||||
"notificationType_new-post-flag": "Коли повідомлення позначено",
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
"popular-month": "Популярні теми цього місяця",
|
||||
"popular-alltime": "Популярні теми за весь час",
|
||||
"recent": "Свіжі теми",
|
||||
"top-day": "Найрейтинговіші теми сьогодні",
|
||||
"top-week": "Найрейтинговіші теми цього тижня",
|
||||
"top-month": "Найрейтинговіші теми цього місяця",
|
||||
"top-alltime": "Найрейтинговіші теми",
|
||||
"top-day": "Top voted topics today",
|
||||
"top-week": "Top voted topics this week",
|
||||
"top-month": "Top voted topics this month",
|
||||
"top-alltime": "Top Voted Topics",
|
||||
"moderator-tools": "Інструменти Модератора",
|
||||
"flagged-content": "Оскаржений вміст",
|
||||
"ip-blacklist": "Чорний список IP адрес",
|
||||
@@ -43,10 +43,10 @@
|
||||
"account/following": "Люди за котрими стежить %1",
|
||||
"account/followers": "Люди котрі стежать за %1",
|
||||
"account/posts": "Пости написані %1",
|
||||
"account/latest-posts": "Останні дописи від %1",
|
||||
"account/latest-posts": "Latest posts made by %1",
|
||||
"account/topics": "Теми створені %1",
|
||||
"account/groups": "Групи %1",
|
||||
"account/watched_categories": "Категорії, за якими спостерігає %1",
|
||||
"account/watched_categories": "%1's Watched Categories",
|
||||
"account/bookmarks": "Закладки %1",
|
||||
"account/settings": "Налаштування користувача",
|
||||
"account/watched": "Теми за якими стежить %1",
|
||||
@@ -56,7 +56,7 @@
|
||||
"account/best": "Найкращі пости %1",
|
||||
"account/blocks": "Заблоковані користувачі для %1",
|
||||
"account/uploads": "Завантаження від %1",
|
||||
"account/sessions": "Логін-сесії",
|
||||
"account/sessions": "Login Sessions",
|
||||
"confirm": "Електронну пошту підтверджено",
|
||||
"maintenance.text": "%1 в данний час на технічному обслуговувані. Завітайте, будь ласка, пізніше.",
|
||||
"maintenance.messageIntro": "Крім того, адміністратор залишив це повідомлення:",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"repeat_password": "Підтвердіть пароль",
|
||||
"enter_email": "Будь ласка, введіть свою <strong>електронну пошту</strong> і ми надішлемо вам листа с інструкцією як скинути ваш обліковий запис.",
|
||||
"enter_email_address": "Введіть електронну пошту",
|
||||
"password_reset_sent": "Якщо зазначена електронна адреса належить існуючому користувачеві, повідомлення для скидання паролю було надіслане на цю адресу. Майте на увазі, що тільки одне повідомлення може бути надіслане за хвилину.",
|
||||
"password_reset_sent": "If the specified address corresponds to an existing user account, a password reset email was sent. Please note that only one email will be sent per minute.",
|
||||
"invalid_email": "Невірна або неіснуюча електронна пошта!",
|
||||
"password_too_short": "Уведений пароль закороткий, оберіть, будь ласка, інший.",
|
||||
"passwords_do_not_match": "Паролі що ви ввели не співпадають.",
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"at-most": "Щонайбільше",
|
||||
"relevance": "Релевантність",
|
||||
"post-time": "Час посту",
|
||||
"votes": "Голоси",
|
||||
"votes": "Votes",
|
||||
"newer-than": "Новіші за",
|
||||
"older-than": "Старіші за",
|
||||
"any-date": "Будь-яка дата",
|
||||
@@ -31,7 +31,7 @@
|
||||
"sort-by": "Сортувати за",
|
||||
"last-reply-time": "Час останньої відповіді",
|
||||
"topic-title": "Заголовок теми",
|
||||
"topic-votes": "Голоси за тему",
|
||||
"topic-votes": "Topic votes",
|
||||
"number-of-replies": "Кількість відповідей",
|
||||
"number-of-views": "Кількість переглядів",
|
||||
"topic-start-date": "Час початку теми",
|
||||
@@ -44,5 +44,5 @@
|
||||
"search-preferences-saved": "Налаштування пошуку збережено",
|
||||
"search-preferences-cleared": "Налаштування пошуку очищені",
|
||||
"show-results-as": "Показати результати як",
|
||||
"see-more-results": "Дивитись більше результатів (%1)"
|
||||
"see-more-results": "See more results (%1)"
|
||||
}
|
||||
@@ -18,13 +18,13 @@
|
||||
"last_reply_time": "Остання відповідь",
|
||||
"reply-as-topic": "Відповісти темою",
|
||||
"guest-login-reply": "Увійти для відповіді",
|
||||
"login-to-view": "🔒 Увійдіть щоб переглянути",
|
||||
"login-to-view": "🔒 Log in to view",
|
||||
"edit": "Редагувати",
|
||||
"delete": "Видалити",
|
||||
"purge": "Стерти",
|
||||
"restore": "Відновити",
|
||||
"move": "Перемістити",
|
||||
"change-owner": "Змінити Власника",
|
||||
"change-owner": "Change Owner",
|
||||
"fork": "Відгалужити",
|
||||
"link": "Зв'язати",
|
||||
"share": "Поширити",
|
||||
@@ -66,7 +66,7 @@
|
||||
"thread_tools.move": "Перемістити тему",
|
||||
"thread_tools.move-posts": "Перемістити Пости",
|
||||
"thread_tools.move_all": "Перемістити всі",
|
||||
"thread_tools.change_owner": "Змінити Власника",
|
||||
"thread_tools.change_owner": "Change Owner",
|
||||
"thread_tools.select_category": "Обрати Категорію",
|
||||
"thread_tools.fork": "Відгалужити тему",
|
||||
"thread_tools.delete": "Видалити тему",
|
||||
@@ -101,7 +101,7 @@
|
||||
"delete_posts_instruction": "Тисніть пости які ви бажаєте видалити/стерти",
|
||||
"merge_topics_instruction": "Натисніть на теми, які потрібно об'єднати",
|
||||
"move_posts_instruction": "Натисніть на пости, які ви хочете перемістити",
|
||||
"change_owner_instruction": "Клікніть на дописи які ви хочете призначити іншому користувачу",
|
||||
"change_owner_instruction": "Click the posts you want to assign to another user",
|
||||
"composer.title_placeholder": "Уведіть заголовок теми...",
|
||||
"composer.handle_placeholder": "Ім'я",
|
||||
"composer.discard": "Скасувати",
|
||||
@@ -134,6 +134,6 @@
|
||||
"diffs.no-revisions-description": "Цей пост має <strong>%1</strong> версій.",
|
||||
"diffs.current-revision": "поточна ревізія",
|
||||
"diffs.original-revision": "початкова ревізія",
|
||||
"timeago_later": "%1 пізніше",
|
||||
"timeago_earlier": "%1 раніше"
|
||||
"timeago_later": "%1 later",
|
||||
"timeago_earlier": "%1 earlier"
|
||||
}
|
||||
@@ -25,17 +25,17 @@
|
||||
"profile_views": "Переглядів профілю",
|
||||
"reputation": "Репутація",
|
||||
"bookmarks": "Закладки",
|
||||
"watched_categories": "Категорії, за якими ви спостерігаєте",
|
||||
"change_all": "Змінити Всі",
|
||||
"watched_categories": "Watched categories",
|
||||
"change_all": "Change All",
|
||||
"watched": "Переглянуті",
|
||||
"ignored": "Ігнорується",
|
||||
"default-category-watch-state": "Спостереження за категоріями за замовчанням",
|
||||
"default-category-watch-state": "Default category watch state",
|
||||
"followers": "Відстежувачі",
|
||||
"following": "Відстежувані",
|
||||
"blocks": "Блокування",
|
||||
"block_toggle": "Увімкнути Блокування",
|
||||
"block_user": "Заблокувати Користувача",
|
||||
"unblock_user": "Розблокувати Користувача",
|
||||
"block_user": "Block User",
|
||||
"unblock_user": "Unblock User",
|
||||
"aboutme": "Про мене",
|
||||
"signature": "Підпис",
|
||||
"birthday": "День народження",
|
||||
@@ -50,7 +50,7 @@
|
||||
"change_picture": "Змінити зображення",
|
||||
"change_username": "Змінити ім'я користувача",
|
||||
"change_email": "Змінити електронну пошту",
|
||||
"email_same_as_password": "Будь-ласка введіть ваш поточний пароль щоб продовжити – ви ввели ваш новий емейл знову",
|
||||
"email_same_as_password": "Please enter your current password to continue – you've entered your new email again",
|
||||
"edit": "Редагувати",
|
||||
"edit-profile": "Редагувати профіль",
|
||||
"default_picture": "Стандартна іконка",
|
||||
@@ -112,9 +112,9 @@
|
||||
"no-sound": "Без звуку",
|
||||
"upvote-notif-freq": "Частота сповіщень позитивних відгуків",
|
||||
"upvote-notif-freq.all": "Всі позитивні відгуки",
|
||||
"upvote-notif-freq.first": "Перше в дописі",
|
||||
"upvote-notif-freq.first": "First Per Post",
|
||||
"upvote-notif-freq.everyTen": "Кожні 10 позитивних відгуків",
|
||||
"upvote-notif-freq.threshold": "На 1, 5, 10, 25, 50, 100, 150, 200...",
|
||||
"upvote-notif-freq.threshold": "On 1, 5, 10, 25, 50, 100, 150, 200...",
|
||||
"upvote-notif-freq.logarithmic": "На 10, 100, 1000...",
|
||||
"upvote-notif-freq.disabled": "Вимкнено",
|
||||
"browsing": "Налаштування перегляду",
|
||||
@@ -125,7 +125,7 @@
|
||||
"follow_topics_you_reply_to": "Підписуватися на теми в котрих ви відповідаєте",
|
||||
"follow_topics_you_create": "Підписуватися на теми які ви створюєте",
|
||||
"grouptitle": "Заголовок групи",
|
||||
"group-order-help": "Оберіть групу і використовуйте стрілки для зміни порядку заголовків",
|
||||
"group-order-help": "Select a group and use the arrows to order titles",
|
||||
"no-group-title": "Немає заголовка групи",
|
||||
"select-skin": "Обрати стиль сайту",
|
||||
"select-homepage": "Обрати домашню сторінку",
|
||||
@@ -152,7 +152,7 @@
|
||||
"info.moderation-note": "Коментар модератора",
|
||||
"info.moderation-note.success": "Коментар модератора збережено",
|
||||
"info.moderation-note.add": "Додати коментар",
|
||||
"sessions.description": "Ця сторінка дозволяє вам переглядати будь-які активні сесії на цьому форумі та видаляти їх якщо потрібно. Ви можете видалити вашу власну сесію, якщо вийдете зі свого акаунта.",
|
||||
"sessions.description": "This page allows you to view any active sessions on this forum and revoke them if necessary. You can revoke your own session by logging out of your account.",
|
||||
"consent.title": "Ваші Права & Згода",
|
||||
"consent.lead": "Цей форум збирає та обробляє вашу особисту інформацію.",
|
||||
"consent.intro": "Ми використовуємо цю інформацію виключно з метою персоналізації вашої активності у цій спільноті, а також для з'єднання ваших постів з вашим особистим акаунтом. На етапі реєстрації ми просили вас надати ім'я користувача та електронну пошту, також ви можете (необов'язково) надати нам додаткову інформацію, щоб завершити створення свого користувацького профілю на цьому сайті.<br /><br />Ми зберігаємо цю інформацію протягом всього періоду життя вашого акаунту, і ви можете відкликати свою згоду у будь-який час, якщо видалите акаунт. У будь-який час ви можете отримати копію ваших особистих даних та внеску на цьому сайті через свою сторінку Права & Згода.<br /><br />Якщо у вас виникли будь-які питання або зауваження, ми заохочуємо вас звернутись до команди Адміністраторів цього форуму.",
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
"filter-by": "Фільтрувати за",
|
||||
"online-only": "Лише в мережі",
|
||||
"invite": "Запросити",
|
||||
"prompt-email": "Емейли:",
|
||||
"prompt-email": "Emails:",
|
||||
"invitation-email-sent": "Лист із запрошенням відправлено %1",
|
||||
"user_list": "Список користувачів",
|
||||
"recent_topics": "Нещодавні теми",
|
||||
|
||||
@@ -1,32 +1,32 @@
|
||||
{
|
||||
"forum-traffic": "Lưu lượng truy cập",
|
||||
"forum-traffic": "Forum Traffic",
|
||||
"page-views": "Lượt xem trang",
|
||||
"unique-visitors": "Khách truy cập duy nhất",
|
||||
"new-users": "Người dùng mới",
|
||||
"new-users": "New Users",
|
||||
"posts": "Bài viết",
|
||||
"topics": "Chủ đề",
|
||||
"page-views-seven": "7 ngày trước",
|
||||
"page-views-thirty": "30 ngày trước",
|
||||
"page-views-last-day": "24 giờ trước",
|
||||
"page-views-custom": "Tùy chỉnh phạm vi ngày",
|
||||
"page-views-custom-start": "Phạm vi bắt đầu",
|
||||
"page-views-custom-end": "Phạm vi kết thúc",
|
||||
"page-views-custom-help": "Nhập phạm vi ngày của lượt xem trang bạn muốn xem. Nếu không có bộ chọn ngày, định dạng được chấp nhận là <code>YYYY-MM-DD</code>",
|
||||
"page-views-custom-error": "Vui lòng nhập một phạm vi ngày hợp lệ trong định dạng <code>YYYY-MM-DD</code>",
|
||||
"page-views-custom": "Custom Date Range",
|
||||
"page-views-custom-start": "Range Start",
|
||||
"page-views-custom-end": "Range End",
|
||||
"page-views-custom-help": "Enter a date range of page views you would like to view. If no date picker is available, the accepted format is <code>YYYY-MM-DD</code>",
|
||||
"page-views-custom-error": "Please enter a valid date range in the format <code>YYYY-MM-DD</code>",
|
||||
|
||||
"stats.yesterday": "Hôm qua",
|
||||
"stats.today": "Hôm nay",
|
||||
"stats.last-week": "Tuần trước",
|
||||
"stats.this-week": "Tuần này",
|
||||
"stats.last-month": "Tháng trước",
|
||||
"stats.this-month": "Tháng này",
|
||||
"stats.all": "Mọi lúc",
|
||||
"stats.yesterday": "Yesterday",
|
||||
"stats.today": "Today",
|
||||
"stats.last-week": "Last Week",
|
||||
"stats.this-week": "This Week",
|
||||
"stats.last-month": "Last Month",
|
||||
"stats.this-month": "This Month",
|
||||
"stats.all": "All Time",
|
||||
|
||||
"updates": "Cập nhật",
|
||||
"running-version": "Bạn đang chạy <strong>NodeBB v<span id=\"version\">%1</span></strong>.",
|
||||
"keep-updated": "Luôn đảm bảo rằng NodeBB của bạn được cập nhật cho các bản vá bảo mật và sửa lỗi mới nhất.",
|
||||
"up-to-date": "<p>Bạn đang <strong>bản mới nhất</strong> <i class=\"fa fa-check\"></i></p>",
|
||||
"upgrade-available": "<p>Phiên bản mới (v%1) đã được phát hành. Xem xét <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">nâng cấp NodeBB của bạn</a>.</p>",
|
||||
"updates": "Updates",
|
||||
"running-version": "You are running <strong>NodeBB v<span id=\"version\">%1</span></strong>.",
|
||||
"keep-updated": "Always make sure that your NodeBB is up to date for the latest security patches and bug fixes.",
|
||||
"up-to-date": "<p>You are <strong>up-to-date</strong> <i class=\"fa fa-check\"></i></p>",
|
||||
"upgrade-available": "<p>A new version (v%1) has been released. Consider <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">upgrading your NodeBB</a>.</p>",
|
||||
"prerelease-upgrade-available": "<p>This is an outdated pre-release version of NodeBB. A new version (v%1) has been released. Consider <a href=\"https://docs.nodebb.org/configuring/upgrade/\" target=\"_blank\">upgrading your NodeBB</a>.</p>",
|
||||
"prerelease-warning": "<p>This is a <strong>pre-release</strong> version of NodeBB. Unintended bugs may occur. <i class=\"fa fa-exclamation-triangle\"></i></p>",
|
||||
"running-in-development": "<span>Forum is running in development mode. The forum may be open to potential vulnerabilities; please contact your system administrator.</span>",
|
||||
@@ -39,7 +39,7 @@
|
||||
"search-plugin-not-installed": "Search Plugin not installed",
|
||||
"search-plugin-tooltip": "Install a search plugin from the plugin page in order to activate search functionality",
|
||||
|
||||
"control-panel": "Điều khiển hệ thống",
|
||||
"control-panel": "System Control",
|
||||
"rebuild-and-restart": "Rebuild & Restart",
|
||||
"restart": "Restart",
|
||||
"restart-warning": "Rebuilding or Restarting your NodeBB will drop all existing connections for a few seconds.",
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
"reorder-plugins": "重新排序插件",
|
||||
"order-active": "排序生效插件",
|
||||
"dev-interested": "有兴趣为NodeBB开发插件?",
|
||||
"docs-info": "有关插件创作的完整文档可以在 <a target=\"_blank\" href=\"https://docs.nodebb-cn.org/development\">NodeBB 文档</a>中找到。",
|
||||
"docs-info": "有关插件创作的完整文档可以在 <a target=\"_blank\" href=\"https://docs.nodebb.org/development/plugins/\">NodeBB 文档</a>中找到。",
|
||||
|
||||
"order.description": "部分插件需要在其它插件启用之后才能完美运作。",
|
||||
"order.explanation": "插件将按照以下顺序载入,从上至下。",
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
"delete": "删除用户",
|
||||
"purge": "删除用户和内容",
|
||||
"download-csv": "下载CSV",
|
||||
"manage-groups": "管理用户组",
|
||||
"add-group": "添加至群组",
|
||||
"manage-groups": "Manage Groups",
|
||||
"add-group": "Add Group",
|
||||
"invite": "邀请",
|
||||
"new": "新建用户",
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@
|
||||
"timestamp": "时间戳",
|
||||
"timestamp.cut-off": "截止日期(天)",
|
||||
"timestamp.cut-off-help": "日期&时间将以相对方式 (例如,“3小时前” / “5天前”) 显示,并且会依照访客语言时区转换。在某一时刻之后,可以切换该文本以显示本地化日期本身 (例如2016年11月5日15:30) 。<br /> <em> (默认值:<code> 30 </code>或一个月) 。 设置为0可始终显示日期,留空以始终显示相对时间。</em>",
|
||||
"timestamp.necro-threshold": "挖坟警告(单位:天)",
|
||||
"timestamp.necro-threshold-help": "若进行回复的帖子最后回复的时间早于挖坟警告设定的天数,则在尝试回复前显示挖坟警告(默认:<code>7</code>天)。可以设置为 0 来禁用。</em>",
|
||||
"timestamp.necro-threshold": "Necro Threshold (in days)",
|
||||
"timestamp.necro-threshold-help": "A message will be shown between posts if the time between them is longer than the necro threshold. (Default: <code>7</code>, or one week). Set to 0 to disable.</em>",
|
||||
"teaser": "预览帖子",
|
||||
"teaser.last-post": "最后– 显示最新的帖子,包括原帖,如果没有回复",
|
||||
"teaser.last-reply": "最后– 显示最新回复,如果没有回复,则显示“无回复”占位符",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"category": "版块",
|
||||
"subcategories": "子版块",
|
||||
"new_topic_button": "发表主题",
|
||||
"new_topic_button": "新主题",
|
||||
"guest-login-post": "登录以发表",
|
||||
"no_topics": "<strong>此版块还没有任何内容。</strong><br />赶紧来发帖吧!",
|
||||
"browsing": "正在浏览",
|
||||
|
||||
@@ -6,15 +6,15 @@
|
||||
"greeting_no_name": "您好",
|
||||
"greeting_with_name": "%1,您好",
|
||||
"email.verify-your-email.subject": "请验证你的电子邮箱",
|
||||
"email.verify.text1": "你的电子邮箱地址已成功更改!",
|
||||
"email.verify.text1": "你的电子邮箱地址已经成功更改!",
|
||||
"welcome.text1": "感谢您注册 %1 帐户!",
|
||||
"welcome.text2": "在您验证您绑定的邮箱地址之后,您的账户才能激活。",
|
||||
"welcome.text3": "管理员批准了您的注册申请,现在您可以登录您的账户了。",
|
||||
"welcome.text2": "我们需要在校验您注册时填写的电子邮箱地址后,才能激活您的帐户。",
|
||||
"welcome.text3": "管理员接受了您的注册请求,请用您的用户名和密码登陆。",
|
||||
"welcome.cta": "点击这里确认您的电子邮箱地址",
|
||||
"invitation.text1": "%1 邀请您加入 %2",
|
||||
"invitation.text2": "您的邀请将在 %1 天后过期。",
|
||||
"invitation.cta": "点击这里新建账号",
|
||||
"reset.text1": "很可能是您忘记了密码,我们收到了重置您帐户密码的申请。 如果您没有申请密码重置,请忽略这封邮件。",
|
||||
"reset.text1": "可能由于您忘记了密码,我们收到了重置您帐户密码的申请。 如果您没有提交密码重置的请求,请忽略这封邮件。",
|
||||
"reset.text2": "如需继续重置密码,请点击下面的链接:",
|
||||
"reset.cta": "点击这里重置您的密码",
|
||||
"reset.notify.subject": "更改密码成功",
|
||||
@@ -43,10 +43,10 @@
|
||||
"test.text1": "这是一封测试邮件,用来验证 NodeBB 的邮件配置是否设置正确。",
|
||||
"unsub.cta": "点击这里修改这些设置",
|
||||
"unsubscribe": "退订",
|
||||
"unsub.success": "您将不再收到来自<strong>%1</strong>邮寄名单的邮件",
|
||||
"banned.subject": "您在 %1 的账户已被封禁",
|
||||
"banned.text1": "您在 %2 的账户 %1 已被封禁。",
|
||||
"banned.text2": "本次封禁将在 %1 结束。",
|
||||
"unsub.success": "你将不再从<strong>%1</strong>邮寄名单接受邮件",
|
||||
"banned.subject": "您已被封禁从 %1",
|
||||
"banned.text1": "用户 %1 已被封禁从 %2.",
|
||||
"banned.text2": "封禁将持续到 %1.",
|
||||
"banned.text3": "这是您被封禁的原因:",
|
||||
"closing": "谢谢!"
|
||||
}
|
||||
@@ -2,11 +2,11 @@
|
||||
"username-email": "用户名 / 邮箱",
|
||||
"username": "用户名",
|
||||
"email": "邮件",
|
||||
"remember_me": "保持登录信息?",
|
||||
"remember_me": "记住我?",
|
||||
"forgot_password": "忘记密码?",
|
||||
"alternative_logins": "使用合作网站帐号登录",
|
||||
"failed_login_attempt": "登录失败",
|
||||
"login_successful": "您已成功登录!",
|
||||
"login_successful": "您已经成功登录!",
|
||||
"dont_have_account": "没有帐号?",
|
||||
"logged-out-due-to-inactivity": "由于长时间不活动,您的账号已被管理员从控制面板中注销"
|
||||
}
|
||||
@@ -465,7 +465,7 @@ define('admin/general/dashboard', ['semver', 'Chart', 'translator', 'benchpress'
|
||||
// Update the View as JSON button url
|
||||
var apiEl = $('#view-as-json');
|
||||
var newHref = $.param({
|
||||
units: units || 'hours',
|
||||
units: units,
|
||||
until: until,
|
||||
count: amount,
|
||||
});
|
||||
|
||||
@@ -227,22 +227,6 @@ define('admin/manage/category', [
|
||||
$('button[data-action="setParent"]').removeClass('hide');
|
||||
});
|
||||
});
|
||||
$('button[data-action="toggle"]').on('click', function () {
|
||||
var payload = {};
|
||||
var $this = $(this);
|
||||
var disabled = $this.attr('data-disabled') === '1';
|
||||
payload[ajaxify.data.category.cid] = {
|
||||
disabled: disabled ? 0 : 1,
|
||||
};
|
||||
socket.emit('admin.categories.update', payload, function (err) {
|
||||
if (err) {
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
$this.translateText(!disabled ? '[[admin/manage/categories:enable]]' : '[[admin/manage/categories:disable]]');
|
||||
$this.toggleClass('btn-primary', !disabled).toggleClass('btn-danger', disabled);
|
||||
$this.attr('data-disabled', disabled ? 0 : 1);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function modified(el) {
|
||||
|
||||
@@ -110,7 +110,6 @@ define('admin/manage/group', [
|
||||
private: $('#group-private').is(':checked'),
|
||||
hidden: $('#group-hidden').is(':checked'),
|
||||
disableJoinRequests: $('#group-disableJoinRequests').is(':checked'),
|
||||
disableLeave: $('#group-disableLeave').is(':checked'),
|
||||
},
|
||||
}, function (err) {
|
||||
if (err) {
|
||||
|
||||
@@ -308,7 +308,6 @@ define('admin/manage/users', ['translator', 'benchpress', 'autocomplete'], funct
|
||||
},
|
||||
});
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -398,7 +398,7 @@ app.cacheBuster = null;
|
||||
}
|
||||
if (registerMessage) {
|
||||
$(document).ready(function () {
|
||||
showAlert('register', utils.escapeHTML(decodeURIComponent(registerMessage)));
|
||||
showAlert('register', decodeURIComponent(registerMessage));
|
||||
registerMessage = false;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -70,10 +70,13 @@ define('chat', [
|
||||
roomData.silent = true;
|
||||
roomData.uid = app.user.uid;
|
||||
roomData.isSelf = isSelf;
|
||||
module.createModal(roomData, function () {
|
||||
module.createModal(roomData, function (modal) {
|
||||
if (!isSelf) {
|
||||
updateTitleAndPlaySound(data.message.mid, username);
|
||||
}
|
||||
if (!modal) {
|
||||
addMessageToModal(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -84,10 +87,7 @@ define('chat', [
|
||||
var username = data.message.fromUser.username;
|
||||
var isSelf = data.self === 1;
|
||||
require(['forum/chats/messages'], function (ChatsMessages) {
|
||||
// don't add if already added
|
||||
if (!modal.find('[data-mid="' + data.message.messageId + '"]').length) {
|
||||
ChatsMessages.appendChatMessage(modal.find('.chat-content'), data.message);
|
||||
}
|
||||
|
||||
if (modal.is(':visible')) {
|
||||
taskbar.updateActive(modal.attr('data-uuid'));
|
||||
@@ -145,7 +145,7 @@ define('chat', [
|
||||
require(['scrollStop', 'forum/chats', 'forum/chats/messages'], function (scrollStop, Chats, ChatsMessages) {
|
||||
app.parseAndTranslate('chat', data, function (chatModal) {
|
||||
if (module.modalExists(data.roomId)) {
|
||||
return callback(module.getModal(data.roomId));
|
||||
return callback(null);
|
||||
}
|
||||
var uuid = utils.generateUUID();
|
||||
var dragged = false;
|
||||
|
||||
@@ -155,7 +155,7 @@ define('taskbar', ['benchpress', 'translator'], function (Benchpress, translator
|
||||
|
||||
var taskbarEl = $('<li />')
|
||||
.addClass(data.options.className)
|
||||
.html('<a href="#"' + (data.options.image ? ' style="background-image: url(\'' + data.options.image + '\'); background-size: cover;"' : '') + '>' +
|
||||
.html('<a href="#"' + (data.options.image ? ' style="background-image: url(\'' + data.options.image + '\');"' : '') + '>' +
|
||||
(data.options.icon ? '<i class="fa ' + data.options.icon + '"></i> ' : '') +
|
||||
'<span component="taskbar/title">' + title + '</span>' +
|
||||
'</a>')
|
||||
|
||||
@@ -361,7 +361,7 @@
|
||||
|
||||
var nodes = descendantTextNodes(element);
|
||||
var text = nodes.map(function (node) {
|
||||
return utils.escapeHTML(node.nodeValue);
|
||||
return node.nodeValue;
|
||||
}).join(' || ');
|
||||
|
||||
var attrNodes = attributes.reduce(function (prev, attr) {
|
||||
|
||||
@@ -123,17 +123,15 @@ if (typeof window !== 'undefined') {
|
||||
$.timeago.settings.allowFuture = true;
|
||||
var userLang = config.userLang.replace('_', '-');
|
||||
var options = { year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||
var formatFn = function (date) {
|
||||
var formatFn;
|
||||
if (typeof Intl === 'undefined') {
|
||||
formatFn = function (date) {
|
||||
return date.toLocaleString(userLang, options);
|
||||
};
|
||||
try {
|
||||
if (typeof Intl !== 'undefined') {
|
||||
} else {
|
||||
var dtFormat = new Intl.DateTimeFormat(userLang, options);
|
||||
formatFn = dtFormat.format;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
var iso;
|
||||
var date;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
if (typeof module === 'object' && module.exports) {
|
||||
var winston = require('winston');
|
||||
|
||||
module.exports = factory(require('xregexp'), winston);
|
||||
module.exports = factory(require('xregexp'), winston, false);
|
||||
module.exports.walk = function (dir, done) {
|
||||
// DEPRECATED
|
||||
var file = require('../../src/file');
|
||||
@@ -21,10 +21,10 @@
|
||||
return (diff[0] * 1e3) + (diff[1] / 1e6);
|
||||
};
|
||||
} else {
|
||||
window.utils = factory(window.XRegExp, console);
|
||||
window.utils = factory(window.XRegExp, console, true);
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
}(function (XRegExp, console) {
|
||||
}(function (XRegExp, console, isBrowser) {
|
||||
var freeze = Object.freeze || function (obj) { return obj; };
|
||||
|
||||
// add default escape function for escaping HTML entities
|
||||
@@ -487,14 +487,50 @@
|
||||
});
|
||||
},
|
||||
|
||||
// https://github.com/sindresorhus/is-absolute-url
|
||||
isAbsoluteUrlRE: /^[a-zA-Z][a-zA-Z\d+\-.]*:/,
|
||||
isWinPathRE: /^[a-zA-Z]:\\/,
|
||||
isAbsoluteUrl: function (url) {
|
||||
if (utils.isWinPathRE.test(url)) {
|
||||
return false;
|
||||
urlToLocation: function (url) {
|
||||
return utils.urlParse(url, true, true);
|
||||
},
|
||||
|
||||
urlParse: function (url, parseQueryString, slashesDenoteHost) {
|
||||
if (isBrowser) {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
return a;
|
||||
}
|
||||
return utils.isAbsoluteUrlRE.test(url);
|
||||
return require('url').parse(url, parseQueryString, slashesDenoteHost);
|
||||
},
|
||||
|
||||
isProtocolAbsoluteUrl: function (url) {
|
||||
url = url.replace(/^\/{3,}/, '//');
|
||||
var a = utils.urlParse(url, true, true);
|
||||
if (isBrowser) {
|
||||
return a.host !== window.location.host;
|
||||
}
|
||||
return !!a.host;
|
||||
},
|
||||
|
||||
isProtocolRelativeUrl: function (url) {
|
||||
return !utils.isProtocolAbsoluteUrl(url);
|
||||
},
|
||||
|
||||
isSchemeAbsoluteUrl: function (url) {
|
||||
var a = utils.urlParse(url);
|
||||
if (isBrowser) {
|
||||
return a.host !== window.location.host;
|
||||
}
|
||||
return !!a.host;
|
||||
},
|
||||
|
||||
isSchemeRelativeUrl: function (url) {
|
||||
return !utils.isSchemeAbsoluteUrl(url);
|
||||
},
|
||||
|
||||
// scheme-absolute seems to win the people's consensus
|
||||
// https://stackoverflow.com/questions/15581445/are-protocol-relative-urls-relative-urls
|
||||
// BUT the behavior is different from the server and the client
|
||||
// so we use isProtocolAbsoluteUrl()
|
||||
isAbsoluteUrl: function (url) {
|
||||
return utils.isProtocolAbsoluteUrl(url);
|
||||
},
|
||||
|
||||
isRelativeUrl: function (url) {
|
||||
@@ -699,12 +735,6 @@
|
||||
return this.params()[key];
|
||||
},
|
||||
|
||||
urlToLocation: function (url) {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
return a;
|
||||
},
|
||||
|
||||
// return boolean if string 'true' or string 'false', or if a parsable string which is a number
|
||||
// also supports JSON object and/or arrays parsing
|
||||
toType: function (str) {
|
||||
|
||||
@@ -8,7 +8,7 @@ const _ = require('lodash');
|
||||
const versions = require('../../admin/versions');
|
||||
const db = require('../../database');
|
||||
const meta = require('../../meta');
|
||||
const analytics = require('../../analytics');
|
||||
const analytics = require('../../analytics').async;
|
||||
const plugins = require('../../plugins');
|
||||
const user = require('../../user');
|
||||
const utils = require('../../utils');
|
||||
@@ -93,7 +93,7 @@ dashboardController.getAnalytics = async (req, res, next) => {
|
||||
}
|
||||
|
||||
const method = req.query.units === 'days' ? analytics.getDailyStatsForSet : analytics.getHourlyStatsForSet;
|
||||
let payload = await Promise.all(sets.map(set => method('analytics:' + set, until, count)));
|
||||
let payload = await Promise.all(sets.map(async set => method('analytics:' + set, until, count)));
|
||||
payload = _.zipObject(sets, payload);
|
||||
|
||||
res.json({
|
||||
|
||||
@@ -86,8 +86,8 @@ apiController.loadConfig = async function (req) {
|
||||
config.usePagination = settings.usePagination;
|
||||
config.topicsPerPage = settings.topicsPerPage;
|
||||
config.postsPerPage = settings.postsPerPage;
|
||||
config.userLang = validator.escape(String((req.query.lang ? req.query.lang : null) || settings.userLang || config.defaultLang));
|
||||
config.acpLang = validator.escape(String((req.query.lang ? req.query.lang : null) || settings.acpLang));
|
||||
config.userLang = (req.query.lang ? validator.escape(String(req.query.lang)) : null) || settings.userLang || config.defaultLang;
|
||||
config.acpLang = (req.query.lang ? validator.escape(String(req.query.lang)) : null) || settings.acpLang;
|
||||
config.openOutgoingLinksInNewTab = settings.openOutgoingLinksInNewTab;
|
||||
config.topicPostSort = settings.topicPostSort || config.topicPostSort;
|
||||
config.categoryTopicSort = settings.categoryTopicSort || config.categoryTopicSort;
|
||||
|
||||
@@ -316,12 +316,8 @@ authenticationController.doLogin = async function (req, uid) {
|
||||
};
|
||||
|
||||
authenticationController.onSuccessfulLogin = async function (req, uid) {
|
||||
/*
|
||||
* Older code required that this method be called from within the SSO plugin.
|
||||
* That behaviour is no longer required, onSuccessfulLogin is now automatically
|
||||
* called in NodeBB core. However, if already called, return prematurely
|
||||
*/
|
||||
if (req.loggedIn && !req.session.forceLogin) {
|
||||
// If already called once, return prematurely
|
||||
if (req.res.locals.user) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,10 +22,6 @@ exports.get = async function (req, res, callback) {
|
||||
templateData: {},
|
||||
});
|
||||
|
||||
if (!data || !data.templateData) {
|
||||
return callback(new Error('[[error:invalid-data]]'));
|
||||
}
|
||||
|
||||
if (data.templateData.disabled) {
|
||||
res.render('', {
|
||||
title: '[[modules:composer.compose]]',
|
||||
|
||||
@@ -166,9 +166,9 @@ async function buildBreadcrumbs(topicData) {
|
||||
}
|
||||
|
||||
async function addTags(topicData, req, res) {
|
||||
const postIndex = parseInt(req.params.post_index, 10) || 0;
|
||||
const postAtIndex = topicData.posts.find(p => parseInt(p.index, 10) === parseInt(Math.max(0, postIndex - 1), 10));
|
||||
let description = '';
|
||||
var postAtIndex = topicData.posts.find(p => parseInt(p.index, 10) === parseInt(Math.max(0, req.params.post_index - 1), 10));
|
||||
|
||||
var description = '';
|
||||
if (postAtIndex && postAtIndex.content) {
|
||||
description = utils.stripHTMLTags(utils.decodeHTMLEntities(postAtIndex.content));
|
||||
}
|
||||
@@ -329,10 +329,10 @@ topicsController.pagination = async function (req, res, callback) {
|
||||
return helpers.notAllowed(req, res);
|
||||
}
|
||||
|
||||
const postCount = topic.postcount;
|
||||
const pageCount = Math.max(1, Math.ceil(postCount / settings.postsPerPage));
|
||||
var postCount = topic.postcount;
|
||||
var pageCount = Math.max(1, Math.ceil(postCount / settings.postsPerPage));
|
||||
|
||||
const paginationData = pagination.create(currentPage, pageCount);
|
||||
var paginationData = pagination.create(currentPage, pageCount);
|
||||
paginationData.rel.forEach(function (rel) {
|
||||
rel.href = nconf.get('url') + '/topic/' + topic.slug + rel.href;
|
||||
});
|
||||
|
||||
@@ -25,7 +25,6 @@ events.types = [
|
||||
'post-delete',
|
||||
'post-restore',
|
||||
'post-purge',
|
||||
'post-change-owner',
|
||||
'topic-delete',
|
||||
'topic-restore',
|
||||
'topic-purge',
|
||||
|
||||
@@ -10,6 +10,7 @@ const graceful = require('graceful-fs');
|
||||
const util = require('util');
|
||||
|
||||
const readdirAsync = util.promisify(fs.readdir);
|
||||
const mkdirpAsync = util.promisify(mkdirp);
|
||||
const copyFileAsync = util.promisify(fs.copyFile);
|
||||
const writeFleAsync = util.promisify(fs.writeFile);
|
||||
const statAsync = util.promisify(fs.stat);
|
||||
@@ -32,7 +33,7 @@ file.saveFileToLocal = async function (filename, folder, tempPath) {
|
||||
const uploadPath = path.join(nconf.get('upload_path'), folder, filename);
|
||||
|
||||
winston.verbose('Saving file ' + filename + ' to : ' + uploadPath);
|
||||
await mkdirp(path.dirname(uploadPath));
|
||||
await mkdirpAsync(path.dirname(uploadPath));
|
||||
await copyFileAsync(tempPath, uploadPath);
|
||||
return {
|
||||
url: '/assets/uploads/' + (folder ? folder + '/' : '') + filename,
|
||||
|
||||
60
src/flags.js
60
src/flags.js
@@ -19,16 +19,6 @@ const utils = require('../public/src/utils');
|
||||
|
||||
const Flags = module.exports;
|
||||
|
||||
Flags._constants = {
|
||||
states: ['open', 'wip', 'resolved', 'rejected'],
|
||||
state_class: {
|
||||
open: 'info',
|
||||
wip: 'warning',
|
||||
resolved: 'success',
|
||||
rejected: 'danger',
|
||||
},
|
||||
};
|
||||
|
||||
Flags.init = async function () {
|
||||
// Query plugins for custom filter strategies and merge into core filter strategies
|
||||
function prepareSets(sets, orSets, prefix, value) {
|
||||
@@ -172,7 +162,13 @@ Flags.list = async function (filters, uid) {
|
||||
'icon:text': userObj['icon:text'],
|
||||
},
|
||||
};
|
||||
flagObj.labelClass = Flags._constants.state_class[flagObj.state];
|
||||
const stateToLabel = {
|
||||
open: 'info',
|
||||
wip: 'warning',
|
||||
resolved: 'success',
|
||||
rejected: 'danger',
|
||||
};
|
||||
flagObj.labelClass = stateToLabel[flagObj.state];
|
||||
|
||||
return Object.assign(flagObj, {
|
||||
description: validator.escape(String(flagObj.description)),
|
||||
@@ -251,21 +247,18 @@ Flags.create = async function (type, id, uid, reason, timestamp) {
|
||||
timestamp = Date.now();
|
||||
doHistoryAppend = true;
|
||||
}
|
||||
const [flagExists, targetExists, canFlag, targetUid, targetCid] = await Promise.all([
|
||||
const [exists, targetExists, targetUid, targetCid] = await Promise.all([
|
||||
// Sanity checks
|
||||
Flags.exists(type, id, uid),
|
||||
Flags.targetExists(type, id),
|
||||
Flags.canFlag(type, id, uid),
|
||||
// Extra data for zset insertion
|
||||
Flags.getTargetUid(type, id),
|
||||
Flags.getTargetCid(type, id),
|
||||
]);
|
||||
if (flagExists) {
|
||||
if (exists) {
|
||||
throw new Error('[[error:already-flagged]]');
|
||||
} else if (!targetExists) {
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
} else if (!canFlag) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
const flagId = await db.incrObjectField('global', 'nextFlagId');
|
||||
|
||||
@@ -310,16 +303,6 @@ Flags.exists = async function (type, id, uid) {
|
||||
return await db.isSortedSetMember('flags:hash', [type, id, uid].join(':'));
|
||||
};
|
||||
|
||||
Flags.canFlag = async function (type, id, uid) {
|
||||
if (type === 'user') {
|
||||
return true;
|
||||
}
|
||||
if (type === 'post') {
|
||||
return await privileges.posts.can('topics:read', id, uid);
|
||||
}
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
};
|
||||
|
||||
Flags.getTarget = async function (type, id, uid) {
|
||||
if (type === 'user') {
|
||||
const userData = await user.getUserData(id);
|
||||
@@ -361,7 +344,6 @@ Flags.getTargetCid = async function (type, id) {
|
||||
};
|
||||
|
||||
Flags.update = async function (flagId, uid, changeset) {
|
||||
const current = await db.getObjectFields('flag:' + flagId, ['state', 'assignee', 'type', 'targetId']);
|
||||
const now = changeset.datetime || Date.now();
|
||||
const notifyAssignee = async function (assigneeId) {
|
||||
if (assigneeId === '' || parseInt(uid, 10) === parseInt(assigneeId, 10)) {
|
||||
@@ -377,43 +359,23 @@ Flags.update = async function (flagId, uid, changeset) {
|
||||
});
|
||||
await notifications.push(notifObj, [assigneeId]);
|
||||
};
|
||||
const isAssignable = async function (assigneeId) {
|
||||
let allowed = false;
|
||||
allowed = await user.isAdminOrGlobalMod(assigneeId);
|
||||
|
||||
// Mods are also allowed to be assigned, if flag target is post in uid's moderated cid
|
||||
if (!allowed && current.type === 'post') {
|
||||
const cid = await posts.getCidByPid(current.targetId);
|
||||
allowed = await user.isModerator(assigneeId, cid);
|
||||
}
|
||||
|
||||
return allowed;
|
||||
};
|
||||
|
||||
// Retrieve existing flag data to compare for history-saving/reference purposes
|
||||
// Retrieve existing flag data to compare for history-saving purposes
|
||||
const current = await db.getObjectFields('flag:' + flagId, ['state', 'assignee']);
|
||||
const tasks = [];
|
||||
for (var prop in changeset) {
|
||||
if (changeset.hasOwnProperty(prop)) {
|
||||
if (current[prop] === changeset[prop]) {
|
||||
delete changeset[prop];
|
||||
} else if (prop === 'state') {
|
||||
if (!Flags._constants.states.includes(changeset[prop])) {
|
||||
delete changeset[prop];
|
||||
} else {
|
||||
tasks.push(db.sortedSetAdd('flags:byState:' + changeset[prop], now, flagId));
|
||||
tasks.push(db.sortedSetRemove('flags:byState:' + current[prop], flagId));
|
||||
}
|
||||
} else if (prop === 'assignee') {
|
||||
/* eslint-disable-next-line */
|
||||
if (!await isAssignable(parseInt(changeset[prop], 10))) {
|
||||
delete changeset[prop];
|
||||
} else {
|
||||
tasks.push(db.sortedSetAdd('flags:byAssignee:' + changeset[prop], now, flagId));
|
||||
tasks.push(notifyAssignee(changeset[prop]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Object.keys(changeset).length) {
|
||||
return;
|
||||
|
||||
@@ -16,7 +16,7 @@ module.exports = function (Groups) {
|
||||
const disableLeave = parseInt(data.disableLeave, 10) === 1 ? 1 : 0;
|
||||
const isHidden = parseInt(data.hidden, 10) === 1;
|
||||
|
||||
Groups.validateGroupName(data.name);
|
||||
validateGroupName(data.name);
|
||||
|
||||
const exists = await meta.userOrGroupExists(data.name);
|
||||
if (exists) {
|
||||
@@ -72,15 +72,11 @@ module.exports = function (Groups) {
|
||||
Groups.isPrivilegeGroup(data.name);
|
||||
}
|
||||
|
||||
Groups.validateGroupName = function (name) {
|
||||
function validateGroupName(name) {
|
||||
if (!name) {
|
||||
throw new Error('[[error:group-name-too-short]]');
|
||||
}
|
||||
|
||||
if (typeof name !== 'string') {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
|
||||
if (!Groups.isPrivilegeGroup(name) && name.length > meta.config.maximumGroupNameLength) {
|
||||
throw new Error('[[error:group-name-too-long]]');
|
||||
}
|
||||
@@ -92,5 +88,5 @@ module.exports = function (Groups) {
|
||||
if (name.includes('/') || !utils.slugify(name)) {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -53,11 +53,6 @@ module.exports = function (Groups) {
|
||||
return Array.isArray(groupsData) && groupsData[0] ? groupsData[0] : null;
|
||||
};
|
||||
|
||||
Groups.getGroupField = async function (groupName, field) {
|
||||
const groupData = await Groups.getGroupFields(groupName, [field]);
|
||||
return groupData ? groupData[field] : null;
|
||||
};
|
||||
|
||||
Groups.getGroupFields = async function (groupName, fields) {
|
||||
const groups = await Groups.getGroupsFields([groupName], fields);
|
||||
return groups ? groups[0] : null;
|
||||
|
||||
@@ -78,8 +78,7 @@ Groups.getGroupsBySort = async function (sort, start, stop) {
|
||||
Groups.getNonPrivilegeGroups = async function (set, start, stop) {
|
||||
let groupNames = await db.getSortedSetRevRange(set, start, stop);
|
||||
groupNames = groupNames.concat(Groups.ephemeralGroups).filter(groupName => !Groups.isPrivilegeGroup(groupName));
|
||||
const groupsData = await Groups.getGroupsData(groupNames);
|
||||
return groupsData.filter(Boolean);
|
||||
return await Groups.getGroupsData(groupNames);
|
||||
};
|
||||
|
||||
Groups.getGroups = async function (set, start, stop) {
|
||||
|
||||
@@ -54,10 +54,7 @@ module.exports = function (Groups) {
|
||||
payload.disableLeave = values.disableLeave ? '1' : '0';
|
||||
}
|
||||
|
||||
if (values.hasOwnProperty('name')) {
|
||||
await checkNameChange(groupName, values.name);
|
||||
}
|
||||
|
||||
if (values.hasOwnProperty('private')) {
|
||||
await updatePrivacy(groupName, values.private);
|
||||
}
|
||||
@@ -128,10 +125,6 @@ module.exports = function (Groups) {
|
||||
}
|
||||
|
||||
async function checkNameChange(currentName, newName) {
|
||||
Groups.validateGroupName(newName);
|
||||
if (Groups.isPrivilegeGroup(newName)) {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
const currentSlug = utils.slugify(currentName);
|
||||
const newSlug = utils.slugify(newName);
|
||||
if (currentName === newName || currentSlug === newSlug) {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const meta = require('../meta');
|
||||
const plugins = require('../plugins');
|
||||
const db = require('../database');
|
||||
const user = require('../user');
|
||||
var meta = require('../meta');
|
||||
var plugins = require('../plugins');
|
||||
var db = require('../database');
|
||||
var user = require('../user');
|
||||
|
||||
module.exports = function (Messaging) {
|
||||
Messaging.sendMessage = async (data) => {
|
||||
@@ -21,7 +21,7 @@ module.exports = function (Messaging) {
|
||||
throw new Error('[[error:invalid-chat-message]]');
|
||||
}
|
||||
|
||||
const maximumChatMessageLength = meta.config.maximumChatMessageLength || 1000;
|
||||
const maximumChatMessageLength = (meta.config.maximumChatMessageLength || 1000);
|
||||
const data = await plugins.fireHook('filter:messaging.checkContent', { content: content });
|
||||
content = String(data.content).trim();
|
||||
if (!content) {
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const validator = require('validator');
|
||||
|
||||
const db = require('../database');
|
||||
const user = require('../user');
|
||||
const utils = require('../utils');
|
||||
const plugins = require('../plugins');
|
||||
var db = require('../database');
|
||||
var user = require('../user');
|
||||
var utils = require('../utils');
|
||||
var plugins = require('../plugins');
|
||||
|
||||
const intFields = ['timestamp', 'edited', 'fromuid', 'roomId', 'deleted', 'system'];
|
||||
|
||||
@@ -81,7 +79,6 @@ module.exports = function (Messaging) {
|
||||
|
||||
messages = await Promise.all(messages.map(async (message) => {
|
||||
if (message.system) {
|
||||
message.content = validator.escape(String(message.content));
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,6 @@ module.exports = function (Messaging) {
|
||||
throw new Error('[[error:chat-' + field + '-already]]');
|
||||
}
|
||||
|
||||
await Messaging.setMessageField(mid, 'deleted', state);
|
||||
return await Messaging.setMessageField(mid, 'deleted', state);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
'use strict';
|
||||
|
||||
const meta = require('../meta');
|
||||
const user = require('../user');
|
||||
var meta = require('../meta');
|
||||
var user = require('../user');
|
||||
|
||||
const sockets = require('../socket.io');
|
||||
var sockets = require('../socket.io');
|
||||
|
||||
|
||||
module.exports = function (Messaging) {
|
||||
@@ -57,18 +57,18 @@ module.exports = function (Messaging) {
|
||||
|
||||
const [isAdmin, messageData] = await Promise.all([
|
||||
user.isAdministrator(uid),
|
||||
Messaging.getMessageFields(messageId, ['fromuid', 'timestamp', 'system']),
|
||||
Messaging.getMessageFields(messageId, ['fromuid', 'timestamp']),
|
||||
]);
|
||||
|
||||
if (isAdmin && !messageData.system) {
|
||||
if (isAdmin) {
|
||||
return;
|
||||
}
|
||||
const chatConfigDuration = meta.config[durationConfig];
|
||||
var chatConfigDuration = meta.config[durationConfig];
|
||||
if (chatConfigDuration && Date.now() - messageData.timestamp > chatConfigDuration * 1000) {
|
||||
throw new Error('[[error:chat-' + type + '-duration-expired, ' + meta.config[durationConfig] + ']]');
|
||||
}
|
||||
|
||||
if (messageData.fromuid === parseInt(uid, 10) && !messageData.system) {
|
||||
if (messageData.fromuid === parseInt(uid, 10)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,8 +139,14 @@ Messaging.getRecentChats = async (callerUid, uid, start, stop) => {
|
||||
});
|
||||
};
|
||||
|
||||
Messaging.generateUsernames = (users, excludeUid) => users.filter(user => user && parseInt(user.uid, 10) !== excludeUid)
|
||||
.map(user => user.username).join(', ');
|
||||
Messaging.generateUsernames = (users, excludeUid) => {
|
||||
users = users.filter(function (user) {
|
||||
return user && parseInt(user.uid, 10) !== excludeUid;
|
||||
});
|
||||
return users.map(function (user) {
|
||||
return user.username;
|
||||
}).join(', ');
|
||||
};
|
||||
|
||||
Messaging.getTeaser = async (uid, roomId) => {
|
||||
const mid = await Messaging.getLatestUndeletedMessage(uid, roomId);
|
||||
|
||||
@@ -20,7 +20,10 @@ module.exports = function (Messaging) {
|
||||
};
|
||||
|
||||
Messaging.getRoomsData = async (roomIds) => {
|
||||
const roomData = await db.getObjects(roomIds.map(roomId => 'chat:room:' + roomId));
|
||||
const roomData = await db.getObjects(roomIds.map(function (roomId) {
|
||||
return 'chat:room:' + roomId;
|
||||
}));
|
||||
|
||||
modifyRoomData(roomData);
|
||||
return roomData;
|
||||
};
|
||||
@@ -50,7 +53,6 @@ module.exports = function (Messaging) {
|
||||
db.sortedSetAdd('chat:room:' + roomId + ':uids', now, uid),
|
||||
]);
|
||||
await Promise.all([
|
||||
Messaging.addSystemMessage('user-join', uid, roomId), // chat owner should also get the user-join system message
|
||||
Messaging.addUsersToRoom(uid, toUids, roomId),
|
||||
Messaging.addRoomToUsers(roomId, [uid].concat(toUids), now),
|
||||
]);
|
||||
@@ -59,7 +61,7 @@ module.exports = function (Messaging) {
|
||||
};
|
||||
|
||||
Messaging.isUserInRoom = async (uid, roomId) => {
|
||||
const inRoom = await db.isSortedSetMember('chat:room:' + roomId + ':uids', uid);
|
||||
const inRoom = db.isSortedSetMember('chat:room:' + roomId + ':uids', uid);
|
||||
const data = await plugins.fireHook('filter:messaging.isUserInRoom', { uid: uid, roomId: roomId, inRoom: inRoom });
|
||||
return data.inRoom;
|
||||
};
|
||||
@@ -111,9 +113,6 @@ module.exports = function (Messaging) {
|
||||
};
|
||||
|
||||
Messaging.leaveRoom = async (uids, roomId) => {
|
||||
const isInRoom = await Promise.all(uids.map(uid => Messaging.isUserInRoom(uid, roomId)));
|
||||
uids = uids.filter((uid, index) => isInRoom[index]);
|
||||
|
||||
const keys = uids
|
||||
.map(uid => 'uid:' + uid + ':chat:rooms')
|
||||
.concat(uids.map(uid => 'uid:' + uid + ':chat:rooms:unread'));
|
||||
@@ -128,9 +127,6 @@ module.exports = function (Messaging) {
|
||||
};
|
||||
|
||||
Messaging.leaveRooms = async (uid, roomIds) => {
|
||||
const isInRoom = await Promise.all(roomIds.map(roomId => Messaging.isUserInRoom(uid, roomId)));
|
||||
roomIds = roomIds.filter((roomId, index) => isInRoom[index]);
|
||||
|
||||
const roomKeys = roomIds.map(roomId => 'chat:room:' + roomId + ':uids');
|
||||
await Promise.all([
|
||||
db.sortedSetsRemove(roomKeys, uid),
|
||||
@@ -196,7 +192,7 @@ module.exports = function (Messaging) {
|
||||
};
|
||||
|
||||
Messaging.canReply = async (roomId, uid) => {
|
||||
const inRoom = await db.isSortedSetMember('chat:room:' + roomId + ':uids', uid);
|
||||
const inRoom = db.isSortedSetMember('chat:room:' + roomId + ':uids', uid);
|
||||
const data = await plugins.fireHook('filter:messaging.canReply', { uid: uid, roomId: roomId, inRoom: inRoom, canReply: inRoom });
|
||||
return data.canReply;
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
const ipaddr = require('ipaddr.js');
|
||||
const winston = require('winston');
|
||||
const _ = require('lodash');
|
||||
const validator = require('validator');
|
||||
|
||||
const db = require('../database');
|
||||
const pubsub = require('../pubsub');
|
||||
@@ -129,7 +128,7 @@ Blacklist.validate = function (rules) {
|
||||
}
|
||||
|
||||
if (!addr || whitelist.includes(rule)) {
|
||||
invalid.push(validator.escape(rule));
|
||||
invalid.push(rule);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
'use strict';
|
||||
|
||||
const os = require('os');
|
||||
const async = require('async');
|
||||
const winston = require('winston');
|
||||
const nconf = require('nconf');
|
||||
@@ -151,14 +150,7 @@ exports.build = function (targets, options, callback) {
|
||||
targets = targets.split(',');
|
||||
}
|
||||
|
||||
let series = nconf.get('series') || options.series;
|
||||
if (series === undefined) {
|
||||
// Detect # of CPUs and select strategy as appropriate
|
||||
winston.verbose('[build] Querying CPU core count for build strategy');
|
||||
const cpus = os.cpus();
|
||||
series = cpus.length < 4;
|
||||
winston.verbose('[build] System returned ' + cpus.length + ' cores, opting for ' + (series ? 'series' : 'parallel') + ' build strategy');
|
||||
}
|
||||
var parallel = !nconf.get('series') && !options.series;
|
||||
|
||||
targets = targets
|
||||
// get full target name
|
||||
@@ -203,14 +195,14 @@ exports.build = function (targets, options, callback) {
|
||||
require('./minifier').maxThreads = threads - 1;
|
||||
}
|
||||
|
||||
if (!series) {
|
||||
if (parallel) {
|
||||
winston.info('[build] Building in parallel mode');
|
||||
} else {
|
||||
winston.info('[build] Building in series mode');
|
||||
}
|
||||
|
||||
startTime = Date.now();
|
||||
buildTargets(targets, !series, next);
|
||||
buildTargets(targets, parallel, next);
|
||||
},
|
||||
function (next) {
|
||||
totalTime = (Date.now() - startTime) / 1000;
|
||||
|
||||
@@ -5,6 +5,7 @@ const path = require('path');
|
||||
const mkdirp = require('mkdirp');
|
||||
const winston = require('winston');
|
||||
const util = require('util');
|
||||
const mkdirpAsync = util.promisify(mkdirp);
|
||||
const writeFileAsync = util.promisify(fs.writeFile);
|
||||
const readFileAsync = util.promisify(fs.readFile);
|
||||
|
||||
@@ -18,7 +19,7 @@ function generate() {
|
||||
}
|
||||
|
||||
exports.write = async function write() {
|
||||
await mkdirp(path.dirname(filePath));
|
||||
await mkdirpAsync(path.dirname(filePath));
|
||||
await writeFileAsync(filePath, generate());
|
||||
};
|
||||
|
||||
|
||||
@@ -3,16 +3,7 @@
|
||||
var path = require('path');
|
||||
var async = require('async');
|
||||
var fs = require('fs');
|
||||
const util = require('util');
|
||||
var mkdirp = require('mkdirp');
|
||||
var mkdirpCallback;
|
||||
if (mkdirp.hasOwnProperty('native')) {
|
||||
mkdirpCallback = util.callbackify(mkdirp);
|
||||
} else {
|
||||
mkdirpCallback = mkdirp;
|
||||
mkdirp = util.promisify(mkdirp);
|
||||
}
|
||||
|
||||
var rimraf = require('rimraf');
|
||||
|
||||
var file = require('../file');
|
||||
@@ -128,7 +119,7 @@ function minifyModules(modules, fork, callback) {
|
||||
return prev;
|
||||
}, []);
|
||||
|
||||
async.each(moduleDirs, mkdirpCallback, function (err) {
|
||||
async.each(moduleDirs, mkdirp, function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@@ -165,7 +156,7 @@ function linkModules(callback) {
|
||||
|
||||
async.parallel({
|
||||
dir: function (cb) {
|
||||
mkdirpCallback(path.dirname(destPath), function (err) {
|
||||
mkdirp(path.dirname(destPath), function (err) {
|
||||
cb(err);
|
||||
});
|
||||
},
|
||||
@@ -281,7 +272,7 @@ JS.linkStatics = function (callback) {
|
||||
var sourceDir = plugins.staticDirs[mappedPath];
|
||||
var destDir = path.join(__dirname, '../../build/public/plugins', mappedPath);
|
||||
|
||||
mkdirpCallback(path.dirname(destDir), function (err) {
|
||||
mkdirp(path.dirname(destDir), function (err) {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
@@ -352,7 +343,7 @@ JS.buildBundle = function (target, fork, callback) {
|
||||
getBundleScriptList(target, next);
|
||||
},
|
||||
function (files, next) {
|
||||
mkdirpCallback(path.join(__dirname, '../../build/public'), function (err) {
|
||||
mkdirp(path.join(__dirname, '../../build/public'), function (err) {
|
||||
next(err, files);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const util = require('util');
|
||||
let mkdirp = require('mkdirp');
|
||||
mkdirp = mkdirp.hasOwnProperty('native') ? mkdirp : util.promisify(mkdirp);
|
||||
const mkdirp = require('mkdirp');
|
||||
const rimraf = require('rimraf');
|
||||
const _ = require('lodash');
|
||||
|
||||
const util = require('util');
|
||||
const mkdirpAsync = util.promisify(mkdirp);
|
||||
const rimrafAsync = util.promisify(rimraf);
|
||||
const writeFileAsync = util.promisify(fs.writeFile);
|
||||
const readFileAsync = util.promisify(fs.readFile);
|
||||
@@ -46,7 +46,7 @@ async function getTranslationMetadata() {
|
||||
|
||||
// save a list of languages to `${buildLanguagesPath}/metadata.json`
|
||||
// avoids readdirs later on
|
||||
await mkdirp(buildLanguagesPath);
|
||||
await mkdirpAsync(buildLanguagesPath);
|
||||
const result = {
|
||||
languages: languages,
|
||||
namespaces: namespaces,
|
||||
@@ -59,7 +59,7 @@ async function writeLanguageFile(language, namespace, translations) {
|
||||
const dev = global.env === 'development';
|
||||
const filePath = path.join(buildLanguagesPath, language, namespace + '.json');
|
||||
|
||||
await mkdirp(path.dirname(filePath));
|
||||
await mkdirpAsync(path.dirname(filePath));
|
||||
await writeFileAsync(filePath, JSON.stringify(translations, null, dev ? 2 : 0));
|
||||
}
|
||||
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const util = require('util');
|
||||
|
||||
const rimraf = require('rimraf');
|
||||
let mkdirp = require('mkdirp');
|
||||
mkdirp = mkdirp.hasOwnProperty('native') ? mkdirp : util.promisify(mkdirp);
|
||||
const mkdirp = require('mkdirp');
|
||||
|
||||
const util = require('util');
|
||||
|
||||
const readdirAsync = util.promisify(fs.readdir);
|
||||
const rimrafAsync = util.promisify(rimraf);
|
||||
const mkdirpAsync = util.promisify(mkdirp);
|
||||
const writeFileAsync = util.promisify(fs.writeFile);
|
||||
|
||||
const file = require('../file');
|
||||
@@ -70,7 +70,7 @@ Sounds.build = async function build() {
|
||||
map.unshift({});
|
||||
map = Object.assign.apply(null, map);
|
||||
await rimrafAsync(soundsPath);
|
||||
await mkdirp(soundsPath);
|
||||
await mkdirpAsync(soundsPath);
|
||||
|
||||
await writeFileAsync(path.join(soundsPath, 'fileMap.json'), JSON.stringify(map));
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
'use strict';
|
||||
|
||||
const util = require('util');
|
||||
let mkdirp = require('mkdirp');
|
||||
mkdirp = mkdirp.hasOwnProperty('native') ? mkdirp : util.promisify(mkdirp);
|
||||
const mkdirp = require('mkdirp');
|
||||
const rimraf = require('rimraf');
|
||||
const winston = require('winston');
|
||||
const path = require('path');
|
||||
|
||||
const util = require('util');
|
||||
const fs = require('fs');
|
||||
const fsReadFile = util.promisify(fs.readFile);
|
||||
const fsWriteFile = util.promisify(fs.writeFile);
|
||||
@@ -123,9 +123,10 @@ Templates.compileTemplate = compileTemplate;
|
||||
|
||||
async function compile() {
|
||||
const _rimraf = util.promisify(rimraf);
|
||||
const _mkdirp = util.promisify(mkdirp);
|
||||
|
||||
await _rimraf(viewsPath);
|
||||
await mkdirp(viewsPath);
|
||||
await _mkdirp(viewsPath);
|
||||
|
||||
let files = await db.getSortedSetRange('plugins:active', 0, -1);
|
||||
files = await getTemplateDirs(files);
|
||||
@@ -136,7 +137,7 @@ async function compile() {
|
||||
let imported = await fsReadFile(filePath, 'utf8');
|
||||
imported = await processImports(files, name, imported);
|
||||
|
||||
await mkdirp(path.join(viewsPath, path.dirname(name)));
|
||||
await _mkdirp(path.join(viewsPath, path.dirname(name)));
|
||||
|
||||
await fsWriteFile(path.join(viewsPath, name), imported);
|
||||
const compiled = await Benchpress.precompile(imported, { minify: global.env !== 'development' });
|
||||
|
||||
@@ -148,13 +148,8 @@ module.exports = function (Plugins) {
|
||||
if (!Array.isArray(hookList) || !hookList.length) {
|
||||
return;
|
||||
}
|
||||
// don't bubble errors from these hooks, so bad plugins don't stop startup
|
||||
const noErrorHooks = ['static:app.load', 'static:assets.prepare', 'static:app.preload'];
|
||||
await async.each(hookList, function (hookObj, next) {
|
||||
if (typeof hookObj.method !== 'function') {
|
||||
return next();
|
||||
}
|
||||
|
||||
if (typeof hookObj.method === 'function') {
|
||||
let timedOut = false;
|
||||
const timeoutId = setTimeout(function () {
|
||||
winston.warn('[plugins] Callback timed out, hook \'' + hook + '\' in plugin \'' + hookObj.id + '\'');
|
||||
@@ -162,14 +157,16 @@ module.exports = function (Plugins) {
|
||||
next();
|
||||
}, 5000);
|
||||
|
||||
const callback = (err) => {
|
||||
clearTimeout(timeoutId);
|
||||
if (err) {
|
||||
const onError = (err) => {
|
||||
winston.error('[plugins] Error executing \'' + hook + '\' in plugin \'' + hookObj.id + '\'');
|
||||
winston.error(err.stack);
|
||||
}
|
||||
winston.error(err);
|
||||
clearTimeout(timeoutId);
|
||||
next();
|
||||
};
|
||||
const callback = (...args) => {
|
||||
clearTimeout(timeoutId);
|
||||
if (!timedOut) {
|
||||
next(noErrorHooks.includes(hook) ? null : err);
|
||||
next(...args);
|
||||
}
|
||||
};
|
||||
try {
|
||||
@@ -177,11 +174,14 @@ module.exports = function (Plugins) {
|
||||
if (utils.isPromise(returned)) {
|
||||
returned.then(
|
||||
payload => setImmediate(callback, null, payload),
|
||||
err => setImmediate(callback, err)
|
||||
err => setImmediate(onError, err)
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
onError(err);
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ module.exports = function (Posts) {
|
||||
};
|
||||
|
||||
Posts.changeOwner = async function (pids, toUid) {
|
||||
const exists = await user.exists(toUid);
|
||||
const exists = user.exists(toUid);
|
||||
if (!exists) {
|
||||
throw new Error('[[error:no-user]]');
|
||||
}
|
||||
@@ -163,7 +163,6 @@ module.exports = function (Posts) {
|
||||
reduceCounters(postsByUser),
|
||||
updateTopicPosters(postData, toUid),
|
||||
]);
|
||||
return postData;
|
||||
};
|
||||
|
||||
async function reduceCounters(postsByUser) {
|
||||
|
||||
@@ -110,7 +110,6 @@ module.exports = function (privileges) {
|
||||
return await utils.promiseParallel({
|
||||
categories: categories.getCategoriesFields(cids, ['disabled']),
|
||||
allowedTo: helpers.isUserAllowedTo(privilege, uid, cids),
|
||||
view_deleted: helpers.isUserAllowedTo('posts:view_deleted', uid, cids),
|
||||
isAdmin: user.isAdministrator(uid),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -88,17 +88,16 @@ module.exports = function (privileges) {
|
||||
cids = _.uniq(cids);
|
||||
|
||||
const results = await privileges.categories.getBase(privilege, cids, uid);
|
||||
const allowedCids = cids.filter(function (cid, index) {
|
||||
cids = cids.filter(function (cid, index) {
|
||||
return !results.categories[index].disabled &&
|
||||
(results.allowedTo[index] || results.isAdmin);
|
||||
});
|
||||
|
||||
const cidsSet = new Set(allowedCids);
|
||||
const canViewDeleted = _.zipObject(cids, results.view_deleted);
|
||||
const cidsSet = new Set(cids);
|
||||
|
||||
pids = postData.filter(function (post) {
|
||||
return post.topic && cidsSet.has(post.topic.cid) &&
|
||||
((!post.topic.deleted && !post.deleted) || canViewDeleted[post.topic.cid] || results.isAdmin);
|
||||
((!post.topic.deleted && !post.deleted) || results.isAdmin);
|
||||
}).map(post => post.pid);
|
||||
|
||||
const data = await plugins.fireHook('filter:privileges.posts.filter', {
|
||||
|
||||
@@ -68,15 +68,14 @@ module.exports = function (privileges) {
|
||||
}
|
||||
|
||||
const topicsData = await topics.getTopicsFields(tids, ['tid', 'cid', 'deleted']);
|
||||
const cids = _.uniq(topicsData.map(topic => topic.cid));
|
||||
let cids = _.uniq(topicsData.map(topic => topic.cid));
|
||||
const results = await privileges.categories.getBase(privilege, cids, uid);
|
||||
|
||||
const allowedCids = cids.filter((cid, index) => !results.categories[index].disabled && (results.allowedTo[index] || results.isAdmin));
|
||||
cids = cids.filter((cid, index) => !results.categories[index].disabled && (results.allowedTo[index] || results.isAdmin));
|
||||
|
||||
const cidsSet = new Set(allowedCids);
|
||||
const canViewDeleted = _.zipObject(cids, results.view_deleted);
|
||||
const cidsSet = new Set(cids);
|
||||
|
||||
tids = topicsData.filter(t => cidsSet.has(t.cid) && (!t.deleted || canViewDeleted[t.cid] || results.isAdmin)).map(t => t.tid);
|
||||
tids = topicsData.filter(t => cidsSet.has(t.cid) && (!t.deleted || results.isAdmin)).map(t => t.tid);
|
||||
|
||||
const data = await plugins.fireHook('filter:privileges.topics.filter', {
|
||||
privilege: privilege,
|
||||
@@ -116,7 +115,7 @@ module.exports = function (privileges) {
|
||||
};
|
||||
|
||||
privileges.topics.canDelete = async function (tid, uid) {
|
||||
const topicData = await topics.getTopicFields(tid, ['uid', 'cid', 'postcount', 'deleterUid']);
|
||||
const topicData = await topics.getTopicFields(tid, ['cid', 'postcount']);
|
||||
const [isModerator, isAdministrator, isOwner, allowedTo] = await Promise.all([
|
||||
user.isModerator(uid, topicData.cid),
|
||||
user.isAdministrator(uid),
|
||||
@@ -136,8 +135,7 @@ module.exports = function (privileges) {
|
||||
throw new Error(langKey);
|
||||
}
|
||||
|
||||
const deleterUid = topicData.deleterUid;
|
||||
return allowedTo[0] && ((isOwner && (deleterUid === 0 || deleterUid === topicData.uid)) || isModerator);
|
||||
return allowedTo[0] && (isOwner || isModerator);
|
||||
};
|
||||
|
||||
privileges.topics.canEdit = async function (tid, uid) {
|
||||
|
||||
@@ -21,7 +21,7 @@ SocketFlags.create = async function (socket, data) {
|
||||
|
||||
const flagObj = await flags.create(data.type, data.id, socket.uid, data.reason);
|
||||
await flags.notify(flagObj, socket.uid);
|
||||
return flagObj.flagId;
|
||||
return flagObj;
|
||||
};
|
||||
|
||||
SocketFlags.update = async function (socket, data) {
|
||||
|
||||
@@ -22,10 +22,6 @@ SocketGroups.join = async (socket, data) => {
|
||||
throw new Error('[[error:invalid-uid]]');
|
||||
}
|
||||
|
||||
if (typeof data.groupName !== 'string') {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
|
||||
if (data.groupName === 'administrators' || groups.isPrivilegeGroup(data.groupName)) {
|
||||
throw new Error('[[error:not-allowed]]');
|
||||
}
|
||||
@@ -70,10 +66,6 @@ SocketGroups.leave = async (socket, data) => {
|
||||
throw new Error('[[error:invalid-uid]]');
|
||||
}
|
||||
|
||||
if (typeof data.groupName !== 'string') {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
|
||||
if (data.groupName === 'administrators') {
|
||||
throw new Error('[[error:cant-remove-self-as-admin]]');
|
||||
}
|
||||
@@ -112,9 +104,6 @@ SocketGroups.addMember = async (socket, data) => {
|
||||
};
|
||||
|
||||
async function isOwner(socket, data) {
|
||||
if (typeof data.groupName !== 'string') {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
const results = await utils.promiseParallel({
|
||||
isAdmin: await user.isAdministrator(socket.uid),
|
||||
isGlobalModerator: await user.isGlobalModerator(socket.uid),
|
||||
@@ -129,9 +118,6 @@ async function isOwner(socket, data) {
|
||||
}
|
||||
|
||||
async function isInvited(socket, data) {
|
||||
if (typeof data.groupName !== 'string') {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
const invited = await groups.isInvited(socket.uid, data.groupName);
|
||||
if (!invited) {
|
||||
throw new Error('[[error:not-invited]]');
|
||||
@@ -185,9 +171,6 @@ SocketGroups.rejectAll = async (socket, data) => {
|
||||
};
|
||||
|
||||
async function acceptRejectAll(method, socket, data) {
|
||||
if (typeof data.groupName !== 'string') {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
const uids = await groups.getPending(data.groupName);
|
||||
await Promise.all(uids.map(async (uid) => {
|
||||
await method(socket, { groupName: data.groupName, toUid: uid });
|
||||
@@ -268,7 +251,7 @@ SocketGroups.kick = async (socket, data) => {
|
||||
SocketGroups.create = async (socket, data) => {
|
||||
if (!socket.uid) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
} else if (typeof data.name !== 'string' || groups.isPrivilegeGroup(data.name)) {
|
||||
} else if (groups.isPrivilegeGroup(data.name)) {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
|
||||
@@ -277,7 +260,6 @@ SocketGroups.create = async (socket, data) => {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
data.ownerUid = socket.uid;
|
||||
data.system = false;
|
||||
const groupData = await groups.create(data);
|
||||
logGroupEvent(socket, 'group-create', {
|
||||
groupName: data.name,
|
||||
@@ -356,6 +338,7 @@ SocketGroups.cover.update = async (socket, data) => {
|
||||
if (!socket.uid) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
await canModifyGroup(socket.uid, data.groupName);
|
||||
return await groups.updateCover(socket.uid, data);
|
||||
};
|
||||
@@ -370,17 +353,12 @@ SocketGroups.cover.remove = async (socket, data) => {
|
||||
};
|
||||
|
||||
async function canModifyGroup(uid, groupName) {
|
||||
if (typeof groupName !== 'string') {
|
||||
throw new Error('[[error:invalid-group-name]]');
|
||||
}
|
||||
const results = await utils.promiseParallel({
|
||||
isOwner: groups.ownership.isOwner(uid, groupName),
|
||||
system: groups.getGroupField(groupName, 'system'),
|
||||
isAdmin: user.isAdministrator(uid),
|
||||
isGlobalMod: user.isGlobalModerator(uid),
|
||||
isAdminOrGlobalMod: user.isAdminOrGlobalMod(uid),
|
||||
});
|
||||
|
||||
if (!(results.isOwner || results.isAdmin || (results.isGlobalMod && !results.system))) {
|
||||
if (!results.isOwner && !results.isAdminOrGlobalMod) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,14 +113,11 @@ SocketModules.chats.getUsersInRoom = async function (socket, data) {
|
||||
if (!data || !data.roomId) {
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
const [isUserInRoom, isOwner, userData] = await Promise.all([
|
||||
Messaging.isUserInRoom(socket.uid, data.roomId),
|
||||
Messaging.isRoomOwner(socket.uid, data.roomId),
|
||||
const [userData, isOwner] = await Promise.all([
|
||||
Messaging.getUsersInRoom(data.roomId, 0, -1),
|
||||
Messaging.isRoomOwner(socket.uid, data.roomId),
|
||||
]);
|
||||
if (!isUserInRoom) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
userData.forEach((user) => {
|
||||
user.canKick = (parseInt(user.uid, 10) !== parseInt(socket.uid, 10)) && isOwner;
|
||||
});
|
||||
|
||||
@@ -167,22 +167,11 @@ module.exports = function (SocketPosts) {
|
||||
if (!data || !Array.isArray(data.pids) || !data.toUid) {
|
||||
throw new Error('[[error:invalid-data]]');
|
||||
}
|
||||
const isAdminOrGlobalMod = await user.isAdminOrGlobalMod(socket.uid);
|
||||
const isAdminOrGlobalMod = user.isAdminOrGlobalMod(socket.uid);
|
||||
if (!isAdminOrGlobalMod) {
|
||||
throw new Error('[[error:no-privileges]]');
|
||||
}
|
||||
|
||||
var postData = await posts.changeOwner(data.pids, data.toUid);
|
||||
var logs = postData.map(({ pid, uid, cid }) => (events.log({
|
||||
type: 'post-change-owner',
|
||||
uid: socket.uid,
|
||||
ip: socket.ip,
|
||||
targetUid: data.toUid,
|
||||
pid: pid,
|
||||
originalUid: uid,
|
||||
cid: cid,
|
||||
})));
|
||||
|
||||
await Promise.all(logs);
|
||||
await posts.changeOwner(data.pids, data.toUid);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -11,7 +11,6 @@ const intFields = [
|
||||
'tid', 'cid', 'uid', 'mainPid', 'postcount',
|
||||
'viewcount', 'deleted', 'locked', 'pinned',
|
||||
'timestamp', 'upvotes', 'downvotes', 'lastposttime',
|
||||
'deleterUid',
|
||||
];
|
||||
|
||||
module.exports = function (Topics) {
|
||||
@@ -91,10 +90,6 @@ function modifyTopic(topic, fields) {
|
||||
|
||||
escapeTitle(topic);
|
||||
|
||||
if (topic.hasOwnProperty('thumb')) {
|
||||
topic.thumb = validator.escape(String(topic.thumb));
|
||||
}
|
||||
|
||||
if (topic.hasOwnProperty('timestamp')) {
|
||||
topic.timestampISO = utils.toISOString(topic.timestamp);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,8 @@ Topics.getTopics = async function (tids, options) {
|
||||
}
|
||||
|
||||
tids = await privileges.topics.filterTids('topics:read', tids, uid);
|
||||
return await Topics.getTopicsByTids(tids, options);
|
||||
const topics = await Topics.getTopicsByTids(tids, options);
|
||||
return topics;
|
||||
};
|
||||
|
||||
Topics.getTopicsByTids = async function (tids, options) {
|
||||
|
||||
@@ -127,7 +127,7 @@ module.exports = function (User) {
|
||||
};
|
||||
|
||||
User.isPasswordValid = function (password, minStrength) {
|
||||
minStrength = (minStrength || minStrength === 0) ? minStrength : meta.config.minimumPasswordStrength;
|
||||
minStrength = minStrength || meta.config.minimumPasswordStrength;
|
||||
|
||||
// Sanity checks: Checks if defined and is string
|
||||
if (!password || !utils.isPasswordValid(password)) {
|
||||
|
||||
@@ -30,8 +30,11 @@ module.exports = function (User) {
|
||||
const now = Date.now();
|
||||
const isArray = Array.isArray(uid);
|
||||
uid = isArray ? uid : [uid];
|
||||
const lastonline = await db.sortedSetScores('users:online', uid);
|
||||
const isOnline = uid.map((uid, index) => (now - lastonline[index]) < (meta.config.onlineCutoff * 60000));
|
||||
const lastonline = db.sortedSetScores('users:online', uid);
|
||||
const isOnline = uid.map(function (uid, index) {
|
||||
return (now - lastonline[index]) < (meta.config.onlineCutoff * 60000);
|
||||
});
|
||||
|
||||
return isArray ? isOnline : isOnline[0];
|
||||
};
|
||||
};
|
||||
|
||||
@@ -23,7 +23,7 @@ module.exports = function (User) {
|
||||
hashedPassword = '';
|
||||
}
|
||||
|
||||
User.isPasswordValid(password, 0);
|
||||
User.isPasswordValid(password);
|
||||
await User.auth.logAttempt(uid, ip);
|
||||
const ok = await Password.compare(password, hashedPassword);
|
||||
if (ok) {
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
const validator = require('validator');
|
||||
|
||||
const meta = require('../meta');
|
||||
const db = require('../database');
|
||||
const plugins = require('../plugins');
|
||||
const notifications = require('../notifications');
|
||||
const languages = require('../languages');
|
||||
|
||||
module.exports = function (User) {
|
||||
User.getSettings = async function (uid) {
|
||||
@@ -58,8 +55,7 @@ module.exports = function (User) {
|
||||
settings.upvoteNotifFreq = getSetting(settings, 'upvoteNotifFreq', 'all');
|
||||
settings.restrictChat = parseInt(getSetting(settings, 'restrictChat', 0), 10) === 1;
|
||||
settings.topicSearchEnabled = parseInt(getSetting(settings, 'topicSearchEnabled', 0), 10) === 1;
|
||||
settings.bootswatchSkin = validator.escape(String(settings.bootswatchSkin || ''));
|
||||
settings.homePageRoute = validator.escape(String(settings.homePageRoute || ''));
|
||||
settings.bootswatchSkin = settings.bootswatchSkin || '';
|
||||
settings.scrollToMyPost = parseInt(getSetting(settings, 'scrollToMyPost', 1), 10) === 1;
|
||||
settings.categoryWatchState = getSetting(settings, 'categoryWatchState', 'notwatching');
|
||||
|
||||
@@ -91,13 +87,6 @@ module.exports = function (User) {
|
||||
throw new Error('[[error:invalid-pagination-value, 2, ' + maxTopicsPerPage + ']]');
|
||||
}
|
||||
|
||||
const languageCodes = await languages.listCodes();
|
||||
if (data.userLang && !languageCodes.includes(data.userLang)) {
|
||||
throw new Error('[[error:invalid-language]]');
|
||||
}
|
||||
if (data.acpLang && !languageCodes.includes(data.acpLang)) {
|
||||
throw new Error('[[error:invalid-language]]');
|
||||
}
|
||||
data.userLang = data.userLang || meta.config.defaultLang;
|
||||
|
||||
plugins.fireHook('action:user.saveSettings', { uid: uid, settings: data });
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user