diff --git a/bcrypt.js b/bcrypt.js index 1445999a8a..ae81d09eb7 100644 --- a/bcrypt.js +++ b/bcrypt.js @@ -9,11 +9,10 @@ process.on('message', function(msg) { if (msg.type === 'hash') { hashPassword(msg.password, msg.rounds); } else if (msg.type === 'compare') { - compare(msg.password, msg.hash); + bcrypt.compare(msg.password, msg.hash, done); } }); - function hashPassword(password, rounds) { async.waterfall([ function(next) { @@ -22,23 +21,14 @@ function hashPassword(password, rounds) { function(salt, next) { bcrypt.hash(password, salt, next); } - ], function(err, hash) { - if (err) { - process.send({err: err.message}); - return process.disconnect(); - } - process.send({result: hash}); - process.disconnect(); - }); + ], done); } -function compare(password, hash) { - bcrypt.compare(password, hash, function(err, res) { - if (err) { - process.send({err: err.message}); - return process.disconnect(); - } - process.send({result: res}); - process.disconnect(); - }); +function done(err, result) { + if (err) { + process.send({err: err.message}); + return process.disconnect(); + } + process.send({result: result}); + process.disconnect(); } \ No newline at end of file diff --git a/package.json b/package.json index 24bc90bef9..7e03c304f3 100644 --- a/package.json +++ b/package.json @@ -39,14 +39,14 @@ "mmmagic": "^0.3.13", "morgan": "^1.3.2", "nconf": "~0.7.1", - "nodebb-plugin-dbsearch": "^0.1.0", + "nodebb-plugin-dbsearch": "^0.2.1", "nodebb-plugin-emoji-extended": "^0.4.1-4", "nodebb-plugin-markdown": "^1.0.0", - "nodebb-plugin-mentions": "^0.10.0", + "nodebb-plugin-mentions": "^0.11.0", "nodebb-plugin-soundpack-default": "~0.1.1", "nodebb-plugin-spam-be-gone": "^0.4.0", - "nodebb-theme-lavender": "^1.0.6", - "nodebb-theme-vanilla": "^1.0.28", + "nodebb-theme-lavender": "^1.0.22", + "nodebb-theme-vanilla": "^1.0.65", "nodebb-widget-essentials": "~0.2.12", "nodebb-rewards-essentials": "^0.0.1", "npm": "^2.1.4", @@ -64,7 +64,7 @@ "socket.io-redis": "^0.1.3", "socketio-wildcard": "~0.1.1", "string": "^3.0.0", - "templates.js": "^0.1.28", + "templates.js": "^0.1.30", "uglify-js": "git+https://github.com/julianlam/UglifyJS2.git", "underscore": "~1.7.0", "validator": "^3.30.0", diff --git a/public/language/en_GB/modules.json b/public/language/en_GB/modules.json index 00a2ba412d..12eeb0f660 100644 --- a/public/language/en_GB/modules.json +++ b/public/language/en_GB/modules.json @@ -18,5 +18,6 @@ "composer.user_said_in": "%1 said in %2:", "composer.user_said": "%1 said:", - "composer.discard": "Are you sure you wish to discard this post?" + "composer.discard": "Are you sure you wish to discard this post?", + "composer.submit_and_lock": "Submit and Lock" } \ No newline at end of file diff --git a/public/language/it/email.json b/public/language/it/email.json index be6567c1c2..e351a25036 100644 --- a/public/language/it/email.json +++ b/public/language/it/email.json @@ -11,7 +11,7 @@ "reset.cta": "Clicca qui per resettare la tua password", "reset.notify.subject": "Possword modificata con successo.", "reset.notify.text1": "Ti informiamo che in data %1, la password è stata cambiata con successo.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", + "reset.notify.text2": "Se non hai autorizzato questo, per favore notifica immediatamente un amministratore.", "digest.notifications": "Hai una notifica non letta da %1:", "digest.latest_topics": "Ultimi argomenti su %1", "digest.cta": "Clicca qui per visitare %1", @@ -21,7 +21,7 @@ "notif.chat.cta": "Clicca qui per continuare la conversazione", "notif.chat.unsub.info": "Questa notifica di chat ti è stata inviata perché l'hai scelta nelle impostazioni.", "notif.post.cta": "Clicca qui per leggere il topic completo.", - "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", + "notif.post.unsub.info": "Questo post ti è stato notificato in base alle tue impostazioni di sottoscrizione.", "test.text1": "Questa è una email di test per verificare che il servizio di invio email è configurato correttamente sul tuo NodeBB.", "unsub.cta": "Clicca qui per modificare queste impostazioni", "closing": "Grazie!" diff --git a/public/language/it/error.json b/public/language/it/error.json index 777c76856c..cbcc20dedc 100644 --- a/public/language/it/error.json +++ b/public/language/it/error.json @@ -19,8 +19,8 @@ "email-taken": "Email già esistente", "email-not-confirmed": "La tua Email deve essere ancora confermata, per favore clicca qui per confermare la tua Email.", "email-not-confirmed-chat": "Non potrai chattare finchè non avrai confermato la tua email", - "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", - "email-confirm-failed": "We could not confirm your email, please try again later.", + "no-email-to-confirm": "Questo forum richiede la conferma dell'indirizzo email, per favore clicca qui per inserirne uno", + "email-confirm-failed": "Non possiamo confermare la tua email, per favore prova ancora più tardi.", "username-too-short": "Nome utente troppo corto", "username-too-long": "Nome utente troppo lungo", "user-banned": "Utente bannato", @@ -77,5 +77,5 @@ "registration-error": "Errore nella registrazione", "parse-error": "Qualcosa è andato storto durante l'analisi della risposta proveniente dal server", "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" + "wrong-login-type-username": "Per favore usa il tuo nome utente per accedere" } \ No newline at end of file diff --git a/public/language/it/notifications.json b/public/language/it/notifications.json index a52aabb969..c3f067b0cf 100644 --- a/public/language/it/notifications.json +++ b/public/language/it/notifications.json @@ -2,7 +2,7 @@ "title": "Notifiche", "no_notifs": "Non hai nuove notifiche", "see_all": "Vedi tutte le Notifiche", - "mark_all_read": "Mark all notifications read", + "mark_all_read": "Segna tutte le notifiche come già lette", "back_to_home": "Indietro a %1", "outgoing_link": "Link in uscita", "outgoing_link_message": "Stai lasciando %1.", diff --git a/public/language/it/pages.json b/public/language/it/pages.json index 72d2a3e042..3d3ed9865a 100644 --- a/public/language/it/pages.json +++ b/public/language/it/pages.json @@ -11,7 +11,7 @@ "user.followers": "Persone che seguono %1", "user.posts": "Post creati da %1", "user.topics": "Discussioni create da %1", - "user.groups": "%1's Groups", + "user.groups": "%1's Gruppi", "user.favourites": "Post Favoriti da %1", "user.settings": "Impostazioni Utente", "maintenance.text": "%1 è attualmente in manutenzione. Per favore ritorna più tardi.", diff --git a/public/language/lt/category.json b/public/language/lt/category.json index 149a102553..3694a767a7 100644 --- a/public/language/lt/category.json +++ b/public/language/lt/category.json @@ -1,9 +1,9 @@ { "new_topic_button": "Nauja tema", - "guest-login-post": "Log in to post", + "guest-login-post": "Prisijungti įrašų paskelbimui", "no_topics": "Šioje kategorijoje temų nėra.
Kodėl gi jums nesukūrus naujos?", "browsing": "naršo", "no_replies": "Niekas dar neatsakė", "share_this_category": "Pasidalinti šią kategoriją", - "ignore": "Ignore" + "ignore": "Nepaisyti" } \ No newline at end of file diff --git a/public/language/lt/email.json b/public/language/lt/email.json index f290435e75..af0968b55e 100644 --- a/public/language/lt/email.json +++ b/public/language/lt/email.json @@ -1,28 +1,28 @@ { "password-reset-requested": "Password Reset Requested - %1!", - "welcome-to": "Welcome to %1", - "greeting_no_name": "Hello", - "greeting_with_name": "Hello %1", + "welcome-to": "Sveiki atvykę į %1", + "greeting_no_name": "Sveiki", + "greeting_with_name": "Sveiki %1", "welcome.text1": "Thank you for registering with %1!", "welcome.text2": "To fully activate your account, we need to verify that you own the email address you registered with.", - "welcome.cta": "Click here to confirm your email address", + "welcome.cta": "El. adreso patvirtinimui spauskite čia", "reset.text1": "We received a request to reset your password, possibly because you have forgotten it. If this is not the case, please ignore this email.", "reset.text2": "To continue with the password reset, please click on the following link:", - "reset.cta": "Click here to reset your password", - "reset.notify.subject": "Password successfully changed", + "reset.cta": "Slaptažodžio atstatymui spauskite čia", + "reset.notify.subject": "Slaptažodis sėkmingai pakeistas", "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", - "digest.notifications": "You have unread notifications from %1:", + "digest.notifications": "Turite neskaitytų pranešimų nuo %1:", "digest.latest_topics": "Latest topics from %1", - "digest.cta": "Click here to visit %1", + "digest.cta": "Kad aplankyti %1, spauskite čia", "digest.unsub.info": "This digest was sent to you due to your subscription settings.", "digest.no_topics": "There have been no active topics in the past %1", "notif.chat.subject": "New chat message received from %1", - "notif.chat.cta": "Click here to continue the conversation", + "notif.chat.cta": "Pokalbio pratęsimui spauskite čia", "notif.chat.unsub.info": "This chat notification was sent to you due to your subscription settings.", - "notif.post.cta": "Click here to read the full topic", + "notif.post.cta": "Spauskite čia norėdami skaityti visą temą", "notif.post.unsub.info": "This post notification was sent to you due to your subscription settings.", "test.text1": "This is a test email to verify that the emailer is set up correctly for your NodeBB.", - "unsub.cta": "Click here to alter those settings", - "closing": "Thanks!" + "unsub.cta": "Spauskite čia norėdami pakeisti šiuos nustatymus", + "closing": "Ačiū!" } \ No newline at end of file diff --git a/public/language/lt/error.json b/public/language/lt/error.json index 295af44ce4..08bf3c80a4 100644 --- a/public/language/lt/error.json +++ b/public/language/lt/error.json @@ -12,26 +12,26 @@ "invalid-title": "Neteisingas pavadinimas!", "invalid-user-data": "Klaidingi vartotojo duomenys", "invalid-password": "Klaidingas slaptažodis", - "invalid-username-or-password": "Please specify both a username and password", - "invalid-search-term": "Invalid search term", + "invalid-username-or-password": "Prašome nurodyti tiek vartotojo vardą, tiek ir slaptažodį", + "invalid-search-term": "Neteisingas paieškos terminas", "invalid-pagination-value": "Klaidinga puslapiavimo reikšmė", "username-taken": "Vartotojo vardas jau užimtas", "email-taken": "El. pašto adresas jau užimtas", - "email-not-confirmed": "Your email has not been confirmed yet, please click here to confirm your email.", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed", - "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", - "email-confirm-failed": "We could not confirm your email, please try again later.", + "email-not-confirmed": "Jūsų el. paštas nepatvirtintas, prašome paspausti čia norint jį patvirtinti.", + "email-not-confirmed-chat": "Jūs negalite kalbėtis, kol el. adresas nepatvirtintas", + "no-email-to-confirm": "Šis forumas reikalauja patvirtinimo el. paštu prašome spausti čia el. adreso įrašymui", + "email-confirm-failed": "Negalime patvirtinti jūsų el. adreso, prašom bandyti vėliau.", "username-too-short": "Slapyvardis per trumpas", - "username-too-long": "Username too long", + "username-too-long": "Vartotojo vardas per ilgas", "user-banned": "Vartotojas užblokuotas", - "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", - "no-category": "Category does not exist", - "no-topic": "Topic does not exist", - "no-post": "Post does not exist", - "no-group": "Group does not exist", - "no-user": "User does not exist", + "user-too-new": "Atsiprašome, bet jums reikia laukti 1% sekundes prieš patalpinant savo pirmąjį įrašą", + "no-category": "Tokios kategorijos nėra", + "no-topic": "Tokios temos nėra", + "no-post": "Tokio įrašo nėra", + "no-group": "Grupė neegzistuoja", + "no-user": "Tokio vartotojo nėra", "no-teaser": "Teaser does not exist", - "no-privileges": "You do not have enough privileges for this action.", + "no-privileges": "Šiam veiksmui jūs neturite pakankamų privilegijų.", "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", "category-disabled": "Kategorija išjungta", "topic-locked": "Tema užrakinta", @@ -50,32 +50,32 @@ "already-favourited": "You have already favourited this post", "already-unfavourited": "You have already unfavourited this post", "cant-ban-other-admins": "Jūs negalite užblokuoti kitų administratorių!", - "invalid-image-type": "Invalid image type. Allowed types are: %1", - "invalid-image-extension": "Invalid image extension", - "invalid-file-type": "Invalid file type. Allowed types are: %1", + "invalid-image-type": "Neteisingas vaizdo tipas. Leidžiami tipai :%1", + "invalid-image-extension": "Neteisingas vaizdo plėtinys", + "invalid-file-type": "Neteisingas failo tipas. Leidžiami tipai: %1", "group-name-too-short": "Grupės pavadinimas per trumpas", "group-already-exists": "Tokia grupė jau egzistuoja", "group-name-change-not-allowed": "Grupės pavadinimas keitimas neleidžiamas", - "group-already-member": "You are already part of this group", + "group-already-member": "Jūs jau priklausote šiai grupei", "group-needs-owner": "This group requires at least one owner", - "post-already-deleted": "This post has already been deleted", - "post-already-restored": "This post has already been restored", - "topic-already-deleted": "This topic has already been deleted", - "topic-already-restored": "This topic has already been restored", + "post-already-deleted": "Šis įrašas jau buvo ištrintas", + "post-already-restored": "Šis įrašas jau atstatytas", + "topic-already-deleted": "Ši tema jau ištrinta", + "topic-already-restored": "Ši tema jau atkurta", "topic-thumbnails-are-disabled": "Temos paveikslėliai neleidžiami.", "invalid-file": "Klaidingas failas", "uploads-are-disabled": "Įkėlimai neleidžiami", "signature-too-long": "Sorry, your signature cannot be longer than %1 characters.", "cant-chat-with-yourself": "Jūs negalite susirašinėti su savimi!", "chat-restricted": "This user has restricted their chat messages. They must follow you before you can chat with them", - "too-many-messages": "You have sent too many messages, please wait awhile.", - "reputation-system-disabled": "Reputation system is disabled.", + "too-many-messages": "Išsiuntėte per daug pranešimų, kurį laiką prašome palaukti.", + "reputation-system-disabled": "Reputacijos sistema išjungta.", "downvoting-disabled": "Downvoting is disabled", "not-enough-reputation-to-downvote": "Jūs neturite pakankamai reputacijos balsuoti prieš šį pranešimą", "not-enough-reputation-to-flag": "You do not have enough reputation to flag this post", "reload-failed": "NodeBB encountered a problem while reloading: \"%1\". NodeBB will continue to serve the existing client-side assets, although you should undo what you did just prior to reloading.", - "registration-error": "Registration Error", + "registration-error": "Registracijos klaida", "parse-error": "Something went wrong while parsing server response", - "wrong-login-type-email": "Please use your email to login", - "wrong-login-type-username": "Please use your username to login" + "wrong-login-type-email": "Prisijungimui prašom naudoti jūsų el. adresą", + "wrong-login-type-username": "Prisijungimui prašome naudoti vartotojo vardą" } \ No newline at end of file diff --git a/public/language/lt/global.json b/public/language/lt/global.json index c4bf81cf4b..8301db2346 100644 --- a/public/language/lt/global.json +++ b/public/language/lt/global.json @@ -27,7 +27,7 @@ "header.tags": "Žymos", "header.popular": "Populiarūs", "header.users": "Vartotojai", - "header.groups": "Groups", + "header.groups": "Grupės", "header.chats": "Susirašinėjimai", "header.notifications": "Pranešimai", "header.search": "Ieškoti", @@ -74,8 +74,8 @@ "guests": "Svečiai", "updated.title": "Forumas atnaujintas", "updated.message": "Forumas buvo atnaujintas iki naujausios versijos. Paspauskite čia norėdami perkrauti puslapį.", - "privacy": "Privacy", - "follow": "Follow", - "unfollow": "Unfollow", - "delete_all": "Delete All" + "privacy": "Privatumas", + "follow": "Sekti", + "unfollow": "Nebesekti", + "delete_all": "Viską ištrinti" } \ No newline at end of file diff --git a/public/language/lt/groups.json b/public/language/lt/groups.json index d2314fdc29..076154cf92 100644 --- a/public/language/lt/groups.json +++ b/public/language/lt/groups.json @@ -1,34 +1,34 @@ { - "groups": "Groups", - "view_group": "View Group", - "owner": "Group Owner", - "new_group": "Create New Group", + "groups": "Grupės", + "view_group": "Grupės peržiūra", + "owner": "Grupės savininkas", + "new_group": "Kurti naują grupę", "no_groups_found": "There are no groups to see", - "pending.accept": "Accept", - "pending.reject": "Reject", - "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", - "cover-change": "Change", - "cover-save": "Save", - "cover-saving": "Saving", - "details.title": "Group Details", - "details.members": "Member List", - "details.pending": "Pending Members", - "details.has_no_posts": "This group's members have not made any posts.", - "details.latest_posts": "Latest Posts", - "details.private": "Private", + "pending.accept": "Priimti", + "pending.reject": "Atmesti", + "cover-instructions": "Vilkite čia nuotrauką, perkelkite į reikalingą poziciją ir spauskite Save", + "cover-change": "Keisti", + "cover-save": "Saugoti", + "cover-saving": "Išsaugoma", + "details.title": "Grupės detalės", + "details.members": "Narių sąrašas", + "details.pending": "Laukiantys nariai", + "details.has_no_posts": "Šios grupės nariai neatliko jokių įrašų.", + "details.latest_posts": "Vėliausi įrašai", + "details.private": "Asmeniška", "details.grant": "Grant/Rescind Ownership", "details.kick": "Kick", "details.owner_options": "Group Administration", - "details.group_name": "Group Name", - "details.description": "Description", + "details.group_name": "Grupės pavadinimas", + "details.description": "Aprašymas", "details.badge_preview": "Badge Preview", - "details.change_icon": "Change Icon", - "details.change_colour": "Change Colour", + "details.change_icon": "Pakeisti paveikslėlį", + "details.change_colour": "Pakeisti spalvą", "details.badge_text": "Badge Text", "details.userTitleEnabled": "Show Badge", "details.private_help": "If enabled, joining of groups requires approval from a group owner", - "details.hidden": "Hidden", + "details.hidden": "Paslėptas", "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", - "event.updated": "Group details have been updated", - "event.deleted": "The group \"%1\" has been deleted" + "event.updated": "Grupės informacija atnaujinta", + "event.deleted": "Grupė \"%1\" pašalinta" } \ No newline at end of file diff --git a/public/language/lt/login.json b/public/language/lt/login.json index ea6dab6e24..dd18d2949c 100644 --- a/public/language/lt/login.json +++ b/public/language/lt/login.json @@ -1,7 +1,7 @@ { - "username-email": "Username / Email", - "username": "Username", - "email": "Email", + "username-email": "Vartotojo vardas / El. paštas", + "username": "Vartotojo vardas", + "email": "El. paštas", "remember_me": "Prisiminti?", "forgot_password": "Užmiršote slaptažodį?", "alternative_logins": "Alternatyvūs prisijungimo būdai", diff --git a/public/language/lt/modules.json b/public/language/lt/modules.json index aefb40aed0..d7e012a412 100644 --- a/public/language/lt/modules.json +++ b/public/language/lt/modules.json @@ -12,9 +12,9 @@ "chat.message-history": "Žinučių istorija", "chat.pop-out": "Iššokančio lango pokalbiai", "chat.maximize": "Padininti", - "chat.seven_days": "7 Days", - "chat.thirty_days": "30 Days", - "chat.three_months": "3 Months", + "chat.seven_days": "7 dienos", + "chat.thirty_days": "30 dienų", + "chat.three_months": "3 mėnesiai", "composer.user_said_in": "%1 parašė į %2:", "composer.user_said": "%1 parašė:", "composer.discard": "Ar tikrai norite sunaikinti šį pranešimą?" diff --git a/public/language/lt/notifications.json b/public/language/lt/notifications.json index 0a38999bff..e7deae258e 100644 --- a/public/language/lt/notifications.json +++ b/public/language/lt/notifications.json @@ -2,12 +2,12 @@ "title": "Pranešimai", "no_notifs": "Jūs neturite naujų pranešimų", "see_all": "Peržiūrėti visus pranešimus", - "mark_all_read": "Mark all notifications read", + "mark_all_read": "Žymėti visus perspėjimus kaip skaitytus", "back_to_home": "Atgal į %1", "outgoing_link": "Išeinanti nuoroda", "outgoing_link_message": "You are now leaving %1.", "continue_to": "Continue to %1", - "return_to": "Return to %1", + "return_to": "Grįžti į %1", "new_notification": "Naujas pranešimas", "you_have_unread_notifications": "Jūs turite neperskaitytų pranešimų.", "new_message_from": "Nauja žinutė nuo %1", @@ -17,7 +17,7 @@ "favourited_your_post_in": "%1 has favourited your post in %2.", "user_flagged_post_in": "%1 flagged a post in %2", "user_posted_to": "%1 parašė atsaką %2", - "user_posted_topic": "%1 has posted a new topic: %2", + "user_posted_topic": "%1 paskelbė naują temą: %2", "user_mentioned_you_in": "%1 paminėjo Jus %2", "user_started_following_you": "%1 started following you.", "email-confirmed": "El. paštas patvirtintas", diff --git a/public/language/lt/pages.json b/public/language/lt/pages.json index 338cd346f5..b317d51487 100644 --- a/public/language/lt/pages.json +++ b/public/language/lt/pages.json @@ -11,9 +11,9 @@ "user.followers": "Žmonės, kurie seka %1", "user.posts": "Pranešimai, kuriuos parašė %1", "user.topics": "Temos, kurias sukūrė %1", - "user.groups": "%1's Groups", + "user.groups": "%1's grupės", "user.favourites": "Vartotojo %1 mėgstami pranešimai", "user.settings": "Vartotojo nustatymai", "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", - "maintenance.messageIntro": "Additionally, the administrator has left this message:" + "maintenance.messageIntro": "Be to, administratorius paliko šį pranešimą:" } \ No newline at end of file diff --git a/public/language/lt/recent.json b/public/language/lt/recent.json index ebb6269a19..bebb9353fa 100644 --- a/public/language/lt/recent.json +++ b/public/language/lt/recent.json @@ -4,16 +4,16 @@ "week": "Savaitė", "month": "Mėnesis", "year": "Metai", - "alltime": "All Time", + "alltime": "Per visą laiką", "no_recent_topics": "Paskutinių temų nėra", - "no_popular_topics": "There are no popular topics.", - "there-is-a-new-topic": "There is a new topic.", - "there-is-a-new-topic-and-a-new-post": "There is a new topic and a new post.", - "there-is-a-new-topic-and-new-posts": "There is a new topic and %1 new posts.", - "there-are-new-topics": "There are %1 new topics.", - "there-are-new-topics-and-a-new-post": "There are %1 new topics and a new post.", - "there-are-new-topics-and-new-posts": "There are %1 new topics and %2 new posts.", - "there-is-a-new-post": "There is a new post.", - "there-are-new-posts": "There are %1 new posts.", - "click-here-to-reload": "Click here to reload." + "no_popular_topics": "Populiarių temų nėra.", + "there-is-a-new-topic": "Yra nauja tema.", + "there-is-a-new-topic-and-a-new-post": "Yra nauja tema ir naujas įrašas.", + "there-is-a-new-topic-and-new-posts": "Yra nauja tema ir %1 nauji įrašai.", + "there-are-new-topics": "Yra %1 naujos temos.", + "there-are-new-topics-and-a-new-post": "Yra %1 naujos temos ir naujas įrašas.", + "there-are-new-topics-and-new-posts": "Yra %1 naujos temos ir %2 nauji įrašai.", + "there-is-a-new-post": "Yra naujas įrašas.", + "there-are-new-posts": "Yra %1 naujas pranešimas.", + "click-here-to-reload": "Spauskite čia norėdami perkrauti." } \ No newline at end of file diff --git a/public/language/lt/reset_password.json b/public/language/lt/reset_password.json index 104f75e6e3..b95f3800be 100644 --- a/public/language/lt/reset_password.json +++ b/public/language/lt/reset_password.json @@ -11,6 +11,6 @@ "enter_email_address": "Įrašykite el. pašto adresą", "password_reset_sent": "Slaptažodžio atstatymas išsiųstas", "invalid_email": "Klaidingas arba neegzistuojantis el. pašto adresas!", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "password_too_short": "Įvestas slaptažodis yra per trumpas, prašome pasirinkti kitą slaptažodį.", + "passwords_do_not_match": "Du slaptažodžiai, kuriuos įvedėte, nesutampa." } \ No newline at end of file diff --git a/public/language/lt/search.json b/public/language/lt/search.json index 9dad8b6eab..b31715a6b4 100644 --- a/public/language/lt/search.json +++ b/public/language/lt/search.json @@ -1,40 +1,40 @@ { "results_matching": "%1 result(s) matching \"%2\", (%3 seconds)", - "no-matches": "No matches found", + "no-matches": "Atitikmenų nerasta", "in": "In", "by": "By", - "titles": "Titles", - "titles-posts": "Titles and Posts", - "posted-by": "Posted by", - "in-categories": "In Categories", + "titles": "Antraštės", + "titles-posts": "Antraštės ir įrašai", + "posted-by": "Parašė", + "in-categories": "Kategorijose", "search-child-categories": "Search child categories", "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", - "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", - "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", - "sort-by": "Sort by", + "at-least": "Mažiausiai", + "at-most": "Daugiausia", + "post-time": "Įrašo laikas", + "newer-than": "Naujesni kaip", + "older-than": "Senesni kaip", + "any-date": "Bet kokia data", + "yesterday": "Vakar", + "one-week": "Viena savaitė", + "two-weeks": "Dvi savaitės", + "one-month": "Mėnuo", + "three-months": "Trys mėnesiai", + "six-months": "Šeši mėnesiai", + "one-year": "Vieneri metai", + "sort-by": "Rūšiuoti pagal", "last-reply-time": "Last reply time", - "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", - "topic-start-date": "Topic start date", - "username": "Username", - "category": "Category", - "descending": "In descending order", - "ascending": "In ascending order", - "save-preferences": "Save preferences", - "clear-preferences": "Clear preferences", - "search-preferences-saved": "Search preferences saved", - "search-preferences-cleared": "Search preferences cleared", - "show-results-as": "Show results as" + "topic-title": "Temos pavadinimas", + "number-of-replies": "Atsakymų skaičius", + "number-of-views": "Peržiūrų skaičius", + "topic-start-date": "Temos pradžia", + "username": "Vartotojo vardas", + "category": "Kategorija", + "descending": "Mažėjančia tvarka", + "ascending": "Didėjančia tvarka", + "save-preferences": "Išsaugoti nustatymus", + "clear-preferences": "Išvalyti nustatymus", + "search-preferences-saved": "Paieškos nustatymai išsaugoti", + "search-preferences-cleared": "Paieškos nuostatos išvalytos", + "show-results-as": "Rodyti rezultatus kaip" } \ No newline at end of file diff --git a/public/language/lt/topic.json b/public/language/lt/topic.json index 5138bc2766..e79c869a1b 100644 --- a/public/language/lt/topic.json +++ b/public/language/lt/topic.json @@ -12,7 +12,7 @@ "notify_me": "Gauti pranešimus apie naujus atsakymus šioje temoje", "quote": "Cituoti", "reply": "Atsakyti", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "Norėdami atsakyti, prisijunkite", "edit": "Redaguoti", "delete": "Ištrinti", "purge": "Išvalyti", @@ -34,11 +34,11 @@ "login_to_subscribe": "Norėdami prenumeruoti šią temą, prašome prisiregistruoti arba prisijungti.", "markAsUnreadForAll.success": "Tema visiems vartotojams pažymėta kaip neskaityta.", "watch": "Žiūrėti", - "unwatch": "Unwatch", + "unwatch": "Nebesekti", "watch.title": "Gauti pranešimą apie naujus įrašus šioje temoje", - "unwatch.title": "Stop watching this topic", + "unwatch.title": "Baigti šios temos stebėjimą", "share_this_post": "Dalintis šiuo įrašu", - "thread_tools.title": "Topic Tools", + "thread_tools.title": "Temos priemonės", "thread_tools.markAsUnreadForAll": "Pažymėti kaip neskaitytą", "thread_tools.pin": "Prisegti temą", "thread_tools.unpin": "Atsegti temą", @@ -48,11 +48,11 @@ "thread_tools.move_all": "Perkelti visus", "thread_tools.fork": "Išskaidyti temą", "thread_tools.delete": "Ištrinti temą", - "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", + "thread_tools.delete_confirm": "Ar jūs tikrai norite ištrinti šią temą?", "thread_tools.restore": "Atkurti temą", - "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", + "thread_tools.restore_confirm": "Ar jūs tikrai norite atkurti šią temą?", "thread_tools.purge": "Išvalyti temą", - "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", + "thread_tools.purge_confirm": "Ar tikrai norite išvalyti šią temą?", "topic_move_success": "Ši tema buvo sėkmingai perkelta į %1", "post_delete_confirm": "Ar jūs tikrai norite ištrinti šį įrašą?", "post_restore_confirm": "Ar jūs tikrai norite atkurti šį įrašą?", @@ -75,7 +75,7 @@ "fork_no_pids": "Nepasirinktas joks įrašas!", "fork_success": "Successfully forked topic! Click here to go to the forked topic.", "composer.title_placeholder": "Įrašykite temos pavadinimą...", - "composer.handle_placeholder": "Name", + "composer.handle_placeholder": "Vardas ir pavardė", "composer.discard": "Atšaukti", "composer.submit": "Patvirtinti", "composer.replying_to": "Atsakymas %1", @@ -90,10 +90,10 @@ "more_users_and_guests": "dar %1 vartotojai(-ų) ir %2 svečiai(-ių)", "more_users": "dar %1 vartotojai(-ų)", "more_guests": "dar %1 svečiai(-ių)", - "users_and_others": "%1 and %2 others", + "users_and_others": "%1 ir kiti %2", "sort_by": "Rūšiuoti pagal", "oldest_to_newest": "Nuo seniausių iki naujausių", "newest_to_oldest": "Nuo naujausių iki seniausių", "most_votes": "Daugiausiai balsų", - "most_posts": "Most posts" + "most_posts": "Daugiausia įrašų" } \ No newline at end of file diff --git a/public/language/lt/user.json b/public/language/lt/user.json index 7ec5d24423..2928033fc2 100644 --- a/public/language/lt/user.json +++ b/public/language/lt/user.json @@ -2,12 +2,12 @@ "banned": "Užblokuotas", "offline": "Atsijungęs", "username": "Vartotojo vardas", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "Prisijungimo data", + "postcount": "Įrašų kiekis", "email": "El. paštas", "confirm_email": "Patvirtinti el. paštą", - "delete_account": "Delete Account", - "delete_account_confirm": "Are you sure you want to delete your account?
This action is irreversible and you will not be able to recover any of your data

Enter your username to confirm that you wish to destroy this account.", + "delete_account": "Ištrinti paskyrą", + "delete_account_confirm": "Ar tikrai norite ištrinti savo paskyrą?
Šis veiksmas yra negrįžtamas, ir jūs negalėsite susigrąžinti jokių duomenų

Įveskite savo vardą, kad patvirtintumėte, jog norite panaikinti šią paskyrą.", "fullname": "Vardas ir pavardė", "website": "Tinklalapis", "location": "Vieta", @@ -32,7 +32,7 @@ "edit": "Redaguoti", "uploaded_picture": "Įkeltas paveikslėlis", "upload_new_picture": "Įkelti naują paveikslėlį", - "upload_new_picture_from_url": "Upload New Picture From URL", + "upload_new_picture_from_url": "Įkelti naują paveikslėlį iš URL", "current_password": "Dabartinis slaptažodis", "change_password": "Pakeisti slaptažodį", "change_password_error": "Negalimas slaptažodis!", @@ -50,21 +50,21 @@ "max": "maks.", "settings": "Nustatymai", "show_email": "Rodyti mano el. paštą viešai", - "show_fullname": "Show My Full Name", - "restrict_chats": "Only allow chat messages from users I follow", + "show_fullname": "Rodyti mano vardą ir pavardę", + "restrict_chats": "Gauti pokalbių žinutes tik iš tų narių, kuriuos seku", "digest_label": "Prenumeruoti įvykių santrauką", "digest_description": "Gauti naujienas apie naujus pranešimus ir temas į el. paštą pagal nustatytą grafiką", "digest_off": "Išjungta", "digest_daily": "Kas dieną", "digest_weekly": "Kas savaitę", "digest_monthly": "Kas mėnesį", - "send_chat_notifications": "Send an email if a new chat message arrives and I am not online", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", + "send_chat_notifications": "Jeigu gaunama nauja pokalbių žinutė ir aš neprisijungęs, siųsti el. laišką", + "send_post_notifications": "Atsiųsti el. laišką kai parašomi atsakymai į mano prenumeruojamas temas", "has_no_follower": "Šis vartotojas neturi jokių sekėjų :(", "follows_no_one": "Šis vartotojas nieko neseka :(", "has_no_posts": "Šis vartotojas dar neparašė nė vieno pranešimo.", "has_no_topics": "Šis vartotojas dar nesukūrė nė vienos temos.", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_watched_topics": "Šis vartotojas dar neseka jokių temų", "email_hidden": "El. paštas paslėptas", "hidden": "paslėptas", "paginate_description": "Temose ir kategorijose naudoti numeraciją puslapiams vietoje begalinės slinkties.", @@ -75,6 +75,6 @@ "open_links_in_new_tab": "Atidaryti nuorodas naujame skirtuke?", "enable_topic_searching": "Enable In-Topic Searching", "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", - "follow_topics_you_reply_to": "Follow topics that you reply to.", - "follow_topics_you_create": "Follow topics you create." + "follow_topics_you_reply_to": "Sekite temas, į kurias jūs atsakėte.", + "follow_topics_you_create": "Sekite jūsų sukurtas temas." } \ No newline at end of file diff --git a/public/language/lt/users.json b/public/language/lt/users.json index 043ca55bcc..f4f4360022 100644 --- a/public/language/lt/users.json +++ b/public/language/lt/users.json @@ -5,8 +5,8 @@ "search": "Ieškoti", "enter_username": "Įrašykite vartotojo vardą paieškai", "load_more": "Įkelti daugiau", - "users-found-search-took": "%1 user(s) found! Search took %2 seconds.", - "filter-by": "Filter By", + "users-found-search-took": "Rasta %1 vartotojas(-ai)! Paieška užtruko %2 sekundes.", + "filter-by": "Filtruoti pagal", "online-only": "Online only", - "picture-only": "Picture only" + "picture-only": "Tik paveikslėlis" } \ No newline at end of file diff --git a/public/language/nb/category.json b/public/language/nb/category.json index 75f38d0df0..f8afafe14a 100644 --- a/public/language/nb/category.json +++ b/public/language/nb/category.json @@ -1,6 +1,6 @@ { "new_topic_button": "Nytt emne", - "guest-login-post": "Log in to post", + "guest-login-post": "Logg inn til innlegg", "no_topics": "Det er ingen emner i denne kategorien
Hvorfor ikke lage ett?", "browsing": "leser", "no_replies": "Ingen har svart", diff --git a/public/language/nb/email.json b/public/language/nb/email.json index 65e1accaee..37b5df85d0 100644 --- a/public/language/nb/email.json +++ b/public/language/nb/email.json @@ -9,9 +9,9 @@ "reset.text1": "Vi har blir bedt om å tilbakestille passordet ditt, muligens fordi du har glemt det. Hvis dette ikke stemmer kan du ignorere denne eposten.", "reset.text2": "For å fortsette med tilbakestillingen, vennligst klikk på følgende lenke:", "reset.cta": "Klikk her for å tilbakestille passordet ditt", - "reset.notify.subject": "Password successfully changed", - "reset.notify.text1": "We are notifying you that on %1, your password was changed successfully.", - "reset.notify.text2": "If you did not authorise this, please notify an administrator immediately.", + "reset.notify.subject": "Passordet ble endret", + "reset.notify.text1": "Vi gir deg beskjed om at du endret passordet ditt den %1.", + "reset.notify.text2": "Hvis du ikke godkjenner dette, vennligst gi beskjed til en administrator omgående.", "digest.notifications": "Du har uleste varsler fra %1:", "digest.latest_topics": "Siste emner fra %1", "digest.cta": "Klikk her for å besøke %1", diff --git a/public/language/nb/error.json b/public/language/nb/error.json index 2fe73f83a4..2b47e8c8ca 100644 --- a/public/language/nb/error.json +++ b/public/language/nb/error.json @@ -19,8 +19,8 @@ "email-taken": "E-post opptatt", "email-not-confirmed": "E-posten din har ikke blitt bekreftet enda, vennligst klikk for å bekrefte din e-post.", "email-not-confirmed-chat": "Du kan ikke chatte før e-posten din har blitt bekreftet", - "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", - "email-confirm-failed": "We could not confirm your email, please try again later.", + "no-email-to-confirm": "Dette forumet krever at e-postbekreftelse, vennligst klikk her for å skrive inn en e-post", + "email-confirm-failed": "Vi kunne ikke godkjenne e-posten din, vennligst prøv igjen senere.", "username-too-short": "Brukernavnet er for kort", "username-too-long": "Brukernavnet er for langt", "user-banned": "Bruker utestengt", @@ -35,7 +35,7 @@ "no-emailers-configured": "Ingen e-post-tillegg er lastet, så ingen test e-post kunne bli sendt", "category-disabled": "Kategori deaktivert", "topic-locked": "Emne låst", - "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", + "post-edit-duration-expired": "Du har bare lov til å endre innlegg i %1 sekunder etter at det ble skrevet", "still-uploading": "Vennligst vent til opplastingene blir fullført.", "content-too-short": "Vennligst skriv et lengere innlegg. Innlegg må inneholde minst %1 tegn.", "content-too-long": "Please enter a shorter post. Posts can't be longer than %1 characters.", diff --git a/public/language/nb/groups.json b/public/language/nb/groups.json index e837ac8961..6ff3a9b71f 100644 --- a/public/language/nb/groups.json +++ b/public/language/nb/groups.json @@ -4,8 +4,8 @@ "owner": "Gruppe-eier", "new_group": "Opprett ny gruppe", "no_groups_found": "Det er ingen grupper å se", - "pending.accept": "Accept", - "pending.reject": "Reject", + "pending.accept": "Aksepter", + "pending.reject": "Avslå", "cover-instructions": "Dra og slipp et bilde, dra til posisjon, og trykk Lagre", "cover-change": "Endre", "cover-save": "Lagre", @@ -15,20 +15,20 @@ "details.pending": "Ventende meldemmer", "details.has_no_posts": "Medlemmene i denne gruppen har ikke skrevet noen innlegg.", "details.latest_posts": "Seneste innlegg", - "details.private": "Private", - "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", + "details.private": "Privat", + "details.grant": "Gi/Opphev Eierskap", + "details.kick": "Kast ut", "details.owner_options": "Gruppeadministrasjon", - "details.group_name": "Group Name", - "details.description": "Description", - "details.badge_preview": "Badge Preview", - "details.change_icon": "Change Icon", - "details.change_colour": "Change Colour", - "details.badge_text": "Badge Text", - "details.userTitleEnabled": "Show Badge", - "details.private_help": "If enabled, joining of groups requires approval from a group owner", - "details.hidden": "Hidden", - "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", + "details.group_name": "Gruppenavn", + "details.description": "Beskrivelse", + "details.badge_preview": "Forhåndsvisning av skilt", + "details.change_icon": "Endre ikon", + "details.change_colour": "Endre farge", + "details.badge_text": "Skilt-tekst", + "details.userTitleEnabled": "Vis skilt", + "details.private_help": "Hvis aktivert, vil medlemskap i grupper kreve godkjennelse fra en gruppe-eier", + "details.hidden": "Skjult", + "details.hidden_help": "vis aktivert, vil denne gruppen ikke bli funnet i gruppelista, og brukere må inviteres manuellt", "event.updated": "Gruppedetaljer har blitt oppgradert", "event.deleted": "Gruppen \"%1\" har blitt slettet" } \ No newline at end of file diff --git a/public/language/nb/login.json b/public/language/nb/login.json index 2b51b10c6a..785a7defc4 100644 --- a/public/language/nb/login.json +++ b/public/language/nb/login.json @@ -1,7 +1,7 @@ { - "username-email": "Username / Email", - "username": "Username", - "email": "Email", + "username-email": "Brukernavn / E-post", + "username": "Brukernavn", + "email": "E-post", "remember_me": "Husk meg?", "forgot_password": "Glemt passord?", "alternative_logins": "Alternativ innlogging", diff --git a/public/language/nb/search.json b/public/language/nb/search.json index 74fdc462a6..2f06a88eb5 100644 --- a/public/language/nb/search.json +++ b/public/language/nb/search.json @@ -3,38 +3,38 @@ "no-matches": "Ingen matcher funnet", "in": "I", "by": "Av", - "titles": "Titles", - "titles-posts": "Titles and Posts", + "titles": "Titler", + "titles-posts": "Titler og innlegg", "posted-by": "Skapt av", - "in-categories": "In Categories", - "search-child-categories": "Search child categories", - "reply-count": "Reply Count", - "at-least": "At least", - "at-most": "At most", - "post-time": "Post time", - "newer-than": "Newer than", - "older-than": "Older than", - "any-date": "Any date", - "yesterday": "Yesterday", - "one-week": "One week", - "two-weeks": "Two weeks", - "one-month": "One month", - "three-months": "Three months", - "six-months": "Six months", - "one-year": "One year", - "sort-by": "Sort by", - "last-reply-time": "Last reply time", - "topic-title": "Topic title", - "number-of-replies": "Number of replies", - "number-of-views": "Number of views", - "topic-start-date": "Topic start date", - "username": "Username", - "category": "Category", + "in-categories": "I kategorier", + "search-child-categories": "Søk underkategorier", + "reply-count": "Mengde svar", + "at-least": "Minst", + "at-most": "Maks", + "post-time": "Innlegg-tid", + "newer-than": "Nyere enn", + "older-than": "Eldre en", + "any-date": "Alle datoer", + "yesterday": "I går", + "one-week": "En uke", + "two-weeks": "To uker", + "one-month": "En måned ", + "three-months": "Tre måneder", + "six-months": "Seks måneder", + "one-year": "Ett år", + "sort-by": "Sorter etter", + "last-reply-time": "Sise svartid", + "topic-title": "Emne-tittel", + "number-of-replies": "Antall svar", + "number-of-views": "Antall visninger", + "topic-start-date": "Starttid for emne", + "username": "Brukernavn", + "category": "Kategori", "descending": "In descending order", "ascending": "In ascending order", - "save-preferences": "Save preferences", - "clear-preferences": "Clear preferences", - "search-preferences-saved": "Search preferences saved", - "search-preferences-cleared": "Search preferences cleared", - "show-results-as": "Show results as" + "save-preferences": "Lagre innstillinger", + "clear-preferences": "Tøm innstillinnger", + "search-preferences-saved": "Søkeinnstillinger lagret", + "search-preferences-cleared": "Søkeinnstillinger tømt", + "show-results-as": "Vis resultateter som" } \ No newline at end of file diff --git a/public/language/th/category.json b/public/language/th/category.json index 387458a0dd..e5d6b1e0fc 100644 --- a/public/language/th/category.json +++ b/public/language/th/category.json @@ -1,9 +1,9 @@ { "new_topic_button": "กระทู้", - "guest-login-post": "Log in to post", + "guest-login-post": "เข้าสู่ระบบเพื่อโพส", "no_topics": "ยังไม่มีกระทู้ในหมวดนี้
โพสต์กระทู้แรก?", "browsing": "เรียกดู", "no_replies": "ยังไม่มีใครตอบ", - "share_this_category": "Share this category", - "ignore": "Ignore" + "share_this_category": "แชร์ Category นี้", + "ignore": "ไม่ต้องสนใจอีก" } \ No newline at end of file diff --git a/public/language/th/error.json b/public/language/th/error.json index 064bd1b530..e265b9cb24 100644 --- a/public/language/th/error.json +++ b/public/language/th/error.json @@ -3,37 +3,37 @@ "not-logged-in": "คุณยังไม่ได้ลงชื่อเข้าระบบ", "account-locked": "บัญชีของคุณถูกระงับการใช้งานชั่วคราว", "search-requires-login": "ต้องลงทะเบียนบัญชีผู้ใช้สำหรับการค้นหา! โปรดลงชื่อเข้าระบบ หรือ ลงทะเบียน!", - "invalid-cid": "Invalid Category ID", - "invalid-tid": "Invalid Topic ID", - "invalid-pid": "Invalid Post ID", - "invalid-uid": "Invalid User ID", - "invalid-username": "Invalid Username", - "invalid-email": "Invalid Email", - "invalid-title": "Invalid title!", - "invalid-user-data": "Invalid User Data", - "invalid-password": "Invalid Password", - "invalid-username-or-password": "Please specify both a username and password", - "invalid-search-term": "Invalid search term", + "invalid-cid": "Category ID ไม่ถูกต้อง", + "invalid-tid": "Topic ID ไม่ถูกต้อง", + "invalid-pid": "Post ID ไม่ถูกต้อง", + "invalid-uid": "User ID ไม่ถูกต้อง", + "invalid-username": "ชื่อผู้ใช้ไม่ถูกต้อง", + "invalid-email": "อีเมลไม่ถูกต้อง", + "invalid-title": "คำนำหน้าชื่อไม่ถูกต้อง", + "invalid-user-data": "User Data ไม่ถูกต้อง", + "invalid-password": "รหัสผ่านไม่ถูกต้อง", + "invalid-username-or-password": "กรุณาระบุชื่อผู้ใช้และรหัสผ่าน", + "invalid-search-term": "ข้อความค้นหาไม่ถูกต้อง", "invalid-pagination-value": "Invalid pagination value", "username-taken": "ชื่อผู้ใช้นี้มีการใช้แล้ว", "email-taken": "อีเมลนี้มีการใช้แล้ว", "email-not-confirmed": "ยังไม่มีการยืนยันอีเมลของคุณ, โปรดกดยืนยันอีเมลของคุณตรงนี้", - "email-not-confirmed-chat": "You are unable to chat until your email is confirmed", - "no-email-to-confirm": "This forum requires email confirmation, please click here to enter an email", - "email-confirm-failed": "We could not confirm your email, please try again later.", + "email-not-confirmed-chat": "คุณไม่สามารถแชทได้ จนกว่าจะได้รับการยืนยันอีเมล", + "no-email-to-confirm": "Forum นี้ต้องการการยืนยันอีเมล กรุณากดที่นี่เพื่อระบุอีเมล", + "email-confirm-failed": "เราไม่สามารถยืนยันอีเมลของคุณ ณ ขณะนี้ กรุณาลองใหม่อีกครั้งภายหลัง", "username-too-short": "ชื่อบัญชีผู้ใช้ สั้นเกินไป", "username-too-long": "ชื่อบัญชีผู้ใช้ ยาวเกินไป", "user-banned": "User banned", "user-too-new": "Sorry, you are required to wait %1 seconds before making your first post", - "no-category": "Category does not exist", - "no-topic": "Topic does not exist", - "no-post": "Post does not exist", - "no-group": "Group does not exist", - "no-user": "User does not exist", + "no-category": "ยังไม่มี Category นี้", + "no-topic": "ยังไม่มี Topic นี้", + "no-post": "ยังไม่มี Post นี้", + "no-group": "ยังไม่มี Group นี้", + "no-user": "ยังไม่มีผู้ใช้งานนี้", "no-teaser": "Teaser does not exist", - "no-privileges": "You do not have enough privileges for this action.", + "no-privileges": "คุณมีสิทธิ์ไม่เพียงพอที่จะทำรายการนี้", "no-emailers-configured": "No email plugins were loaded, so a test email could not be sent", - "category-disabled": "Category disabled", + "category-disabled": "Category นี้ถูกปิดการใช้งานแล้ว", "topic-locked": "Topic Locked", "post-edit-duration-expired": "You are only allowed to edit posts for %1 seconds after posting", "still-uploading": "Please wait for uploads to complete.", diff --git a/public/language/th/global.json b/public/language/th/global.json index af4ff3bfd0..d81b58b8eb 100644 --- a/public/language/th/global.json +++ b/public/language/th/global.json @@ -14,20 +14,20 @@ "please_log_in": "กรุณาเข้าสู่ระบบ", "logout": "ออกจากระบบ", "posting_restriction_info": "คุณต้องต้องเป็นสมาชิกเพื่อทำการโพสต์ คลิกที่นี่เพื่อเข้าสู่ระบบ", - "welcome_back": "Welcome Back", + "welcome_back": "ยินดีต้อนรับ", "you_have_successfully_logged_in": "คุณได้เข้าสู่ระบบแล้ว", "save_changes": "บันทึกการเปลี่ยนแปลง", "close": "ปิด", "pagination": "ให้เลขหน้า", - "pagination.out_of": "%1 out of %2", + "pagination.out_of": "%1 จาก %2", "pagination.enter_index": "Enter index", "header.admin": "ผู้ดูแลระบบ", "header.recent": "ล่าสุด", "header.unread": "ไม่ได้อ่าน", - "header.tags": "Tags", + "header.tags": "Tag", "header.popular": "ฮิต", "header.users": "ผู้ใช้", - "header.groups": "Groups", + "header.groups": "Group", "header.chats": "สนทนา", "header.notifications": "แจ้งเตือน", "header.search": "ค้นหา", diff --git a/public/language/th/groups.json b/public/language/th/groups.json index d2314fdc29..be94efdf0b 100644 --- a/public/language/th/groups.json +++ b/public/language/th/groups.json @@ -1,34 +1,34 @@ { - "groups": "Groups", - "view_group": "View Group", - "owner": "Group Owner", - "new_group": "Create New Group", - "no_groups_found": "There are no groups to see", - "pending.accept": "Accept", - "pending.reject": "Reject", - "cover-instructions": "Drag and Drop a photo, drag to position, and hit Save", - "cover-change": "Change", - "cover-save": "Save", - "cover-saving": "Saving", - "details.title": "Group Details", - "details.members": "Member List", - "details.pending": "Pending Members", - "details.has_no_posts": "This group's members have not made any posts.", - "details.latest_posts": "Latest Posts", - "details.private": "Private", + "groups": "Group", + "view_group": "ดู Group", + "owner": "เจ้าของ Group", + "new_group": "สร้าง Group ใหม่", + "no_groups_found": "ยังไม่มี Group", + "pending.accept": "ยอมรับ", + "pending.reject": "ไม่ยอมรับ", + "cover-instructions": "ลากรูปภาพไปวางยังตำแหน่งที่ต้องการแล้วกดที่ปุ่ม บันทึก", + "cover-change": "ปรับปรุง", + "cover-save": "บันทึก", + "cover-saving": "กำลังบันทึก", + "details.title": "ข้อมูล Group", + "details.members": "รายชื่อสมาชิก", + "details.pending": "สมาชิกที่กำลังรอการตอบรับ", + "details.has_no_posts": "Group นี้ยังไม่มีโพสจากสมาชิก", + "details.latest_posts": "โพสล่าสุด", + "details.private": "ส่วนตัว", "details.grant": "Grant/Rescind Ownership", - "details.kick": "Kick", - "details.owner_options": "Group Administration", - "details.group_name": "Group Name", - "details.description": "Description", + "details.kick": "เตะออก", + "details.owner_options": "การจัดการ Group", + "details.group_name": "ชื่อ Group", + "details.description": "คำอธิบาย", "details.badge_preview": "Badge Preview", - "details.change_icon": "Change Icon", - "details.change_colour": "Change Colour", + "details.change_icon": "เปลี่ยนไอคอน", + "details.change_colour": "เปลี่ยนสี", "details.badge_text": "Badge Text", - "details.userTitleEnabled": "Show Badge", + "details.userTitleEnabled": "แสดง Badge", "details.private_help": "If enabled, joining of groups requires approval from a group owner", - "details.hidden": "Hidden", + "details.hidden": "ซ่อน", "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", - "event.updated": "Group details have been updated", + "event.updated": "ข้อมูล Group ได้รับการบันทึกแล้ว", "event.deleted": "The group \"%1\" has been deleted" } \ No newline at end of file diff --git a/public/language/th/login.json b/public/language/th/login.json index f0aff3c796..5eba336a39 100644 --- a/public/language/th/login.json +++ b/public/language/th/login.json @@ -1,7 +1,7 @@ { - "username-email": "Username / Email", - "username": "Username", - "email": "Email", + "username-email": "ชื่อผู้ใช้ / อีเมล", + "username": "ชื่อผู้ใช้", + "email": "อีเมล", "remember_me": "จำไว้ในระบบ?", "forgot_password": "ลืมรหัสผ่าน?", "alternative_logins": "เข้าสู่ระบบโดยทางอื่น", diff --git a/public/language/th/pages.json b/public/language/th/pages.json index 44f8b127e1..4b4998d3bd 100644 --- a/public/language/th/pages.json +++ b/public/language/th/pages.json @@ -5,15 +5,15 @@ "recent": "กระทู้ล่าสุด", "users": "ผู้ใช้ที่ลงทะเบียน", "notifications": "แจ้งเตือน", - "tags": "Topics tagged under \"%1\"", + "tags": "หัวข้อที่ถูก Tag อยู่ภายใต้ \"%1\"", "user.edit": "แก้ไข \"%1\"", "user.following": "ผู้ใช้ที่ %1 ติดตาม", "user.followers": "ผู้ใช้ที่ติดตาม %1", "user.posts": "กระทู้โดย %1", - "user.topics": "Topics created by %1", - "user.groups": "%1's Groups", + "user.topics": "หัวข้อที่ถูกสร้างโดย %1", + "user.groups": "กลุ่มของ %1", "user.favourites": "กระทู้ที่ %1 ชอบ", "user.settings": "ตั้งค่าผู้ใช้", - "maintenance.text": "%1 is currently undergoing maintenance. Please come back another time.", - "maintenance.messageIntro": "Additionally, the administrator has left this message:" + "maintenance.text": "%1 กำลังอยู่ระหว่างการปิดปรับปรุงชั่วคราว กรุณาลองใหม่อีกครั้งในภายหลัง", + "maintenance.messageIntro": "ผู้ดูแลระบบได้ฝากข้อความต่อไปนี้เอาไว้" } \ No newline at end of file diff --git a/public/language/th/reset_password.json b/public/language/th/reset_password.json index 499f2f7136..1b66d7dd33 100644 --- a/public/language/th/reset_password.json +++ b/public/language/th/reset_password.json @@ -11,6 +11,6 @@ "enter_email_address": "ใส่อีเมล์", "password_reset_sent": "รหัสรีเซ็ตถูกส่งออกไปแล้ว", "invalid_email": "อีเมล์ไม่ถูกต้อง / อีเมล์ไม่มีอยู่!", - "password_too_short": "The password entered is too short, please pick a different password.", - "passwords_do_not_match": "The two passwords you've entered do not match." + "password_too_short": "รหัสผ่านที่คุณกำหนดยังสั้นเกินไป กรุณากำหนดรหัสผ่านของคุณใหม่", + "passwords_do_not_match": "รหัสผ่านทั้ง 2 ที่ใส่ไม่ตรงกัน" } \ No newline at end of file diff --git a/public/language/th/tags.json b/public/language/th/tags.json index d2d0ff6fe0..bdb45d5eef 100644 --- a/public/language/th/tags.json +++ b/public/language/th/tags.json @@ -1,7 +1,7 @@ { "no_tag_topics": "ไม่มีหัวข้อสนทนาที่เกี่ยวข้องกับป้ายคำศัพท์นี้", "tags": "ป้ายคำศัพท์", - "enter_tags_here": "Enter tags here. %1-%2 characters. Press enter after each tag.", + "enter_tags_here": "ใส่ Tags ตรงนี้ได้ทั้งหมด %1-%2 ตัวอักษร และกรุณากดปุ่ม Enter ทุกครั้งเมื่อต้องการเพิ่ม Tag ใหม่", "enter_tags_here_short": "ใส่ป้ายคำศัพท์ ...", "no_tags": "ยังไม่มีป้ายคำศัพท์" } \ No newline at end of file diff --git a/public/language/th/topic.json b/public/language/th/topic.json index 652ebe5db5..ea6a1a8998 100644 --- a/public/language/th/topic.json +++ b/public/language/th/topic.json @@ -4,15 +4,15 @@ "topic_id_placeholder": "Enter topic ID", "no_topics_found": "ไม่พบกระทู้", "no_posts_found": "ไม่พบโพส", - "post_is_deleted": "This post is deleted!", + "post_is_deleted": "ลบ Post นี้เรียบร้อยแล้ว!", "profile": "รายละเอียด", - "posted_by": "Posted by %1", - "posted_by_guest": "Posted by Guest", + "posted_by": "โพสโดย %1", + "posted_by_guest": "โพสโดย Guest", "chat": "แชท", "notify_me": "แจ้งเตือนเมื่อการตอบใหม่ในกระทู้นี้", "quote": "คำอ้างอิง", "reply": "ตอบ", - "guest-login-reply": "Log in to reply", + "guest-login-reply": "เข้าสู่ระบบเพื่อตอบกลับ", "edit": "แก้ไข", "delete": "ลบ", "purge": "Purge", @@ -26,31 +26,31 @@ "locked": "Locked", "bookmark_instructions": "คลิกที่นี่เพื่อกลับคืนสู่ฐานะสุดท้าย หรือ คลิกปิดเพื่อยกเลิก", "flag_title": "ปักธงโพสต์นี้เพื่อดำเนินการ", - "flag_confirm": "Are you sure you want to flag this post?", + "flag_confirm": "มั่นใจแล้วหรือไม่ที่จะ Flag Post นี้?", "flag_success": "This post has been flagged for moderation.", - "deleted_message": "This topic has been deleted. Only users with topic management privileges can see it.", + "deleted_message": "Topic นี้ถูกลบไปแล้ว เฉพาะผู้ใช้งานที่มีสิทธิ์ในการจัดการ Topic เท่านั้นที่จะมีสิทธิ์ในการเข้าชม", "following_topic.message": "คุณจะได้รับการแจ้งเตือนเมื่อมีคนโพสต์ในกระทู้นี้", "not_following_topic.message": "คุณจะไม่รับการแจ้งเตือนจากกระทู้นี้", "login_to_subscribe": "กรุณาลงทะเบียนหรือเข้าสู่ระบบเพื่อที่จะติดตามกระทู้นี้", "markAsUnreadForAll.success": "ทำเครื่องหมายว่ายังไม่ได้อ่านทั้งหมด", "watch": "ติดตาม", - "unwatch": "Unwatch", - "watch.title": "Be notified of new replies in this topic", - "unwatch.title": "Stop watching this topic", + "unwatch": "ยังไม่ได้ติดตาม", + "watch.title": "ให้แจ้งเตือนเมื่อมีการตอบกลับ Topic นี้", + "unwatch.title": "ยกเลิกการติดตาม Topic นี้", "share_this_post": "แชร์โพสต์นี้", - "thread_tools.title": "Topic Tools", + "thread_tools.title": "เครื่องมือช่วยจัดการ Topic", "thread_tools.markAsUnreadForAll": "ทำหมายว่ายังไม่ได้อ่าน", "thread_tools.pin": "ปักหมุดกระทู้", "thread_tools.unpin": "เลิกปักหมุดกระทู้", "thread_tools.lock": "ล็อคกระทู้", "thread_tools.unlock": "ปลดล็อคกระทู้", "thread_tools.move": "ย้ายกระทู้", - "thread_tools.move_all": "Move All", + "thread_tools.move_all": "ย้ายทั้งหมด", "thread_tools.fork": "แยกกระทู้", "thread_tools.delete": "ลบกระทู้", - "thread_tools.delete_confirm": "Are you sure you want to delete this topic?", + "thread_tools.delete_confirm": "มั่นใจแล้วหรือไม่ที่จะลบ Topic นี้?", "thread_tools.restore": "กู้กระทู้", - "thread_tools.restore_confirm": "Are you sure you want to restore this topic?", + "thread_tools.restore_confirm": "มั่นใจแล้วหรือไม่ที่จะกู้คืน Topic นี้?", "thread_tools.purge": "Purge Topic", "thread_tools.purge_confirm": "Are you sure you want to purge this topic?", "topic_move_success": "This topic has been successfully moved to %1", diff --git a/public/language/th/unread.json b/public/language/th/unread.json index 37451a65ba..2595955dc7 100644 --- a/public/language/th/unread.json +++ b/public/language/th/unread.json @@ -2,8 +2,8 @@ "title": "ไม่ได้อ่าน", "no_unread_topics": "ไม่มีกระทู้ที่ยังไม่ได้อ่านเป็น", "load_more": "โหลดเพิ่มเติม", - "mark_as_read": "Mark as Read", - "selected": "Selected", - "all": "All", - "topics_marked_as_read.success": "Topics marked as read!" + "mark_as_read": "ทำเครื่องหมายว่าอ่านแล้ว", + "selected": "เลือก", + "all": "ทั้งหมด", + "topics_marked_as_read.success": "Topic ถูกทำเครื่องหมายว่าอ่านแล้วเรียบร้อย" } \ No newline at end of file diff --git a/public/language/th/user.json b/public/language/th/user.json index aaf5a74424..0e8de83da7 100644 --- a/public/language/th/user.json +++ b/public/language/th/user.json @@ -2,8 +2,8 @@ "banned": "เเบน", "offline": "ออฟไลน์", "username": "ชื่อผู้ใช้", - "joindate": "Join Date", - "postcount": "Post Count", + "joindate": "วันที่เข้าร่วม", + "postcount": "จำนวนโพส", "email": "อีเมล์", "confirm_email": "ยืนยันอีเมล", "delete_account": "ลบบัญชี", @@ -18,7 +18,7 @@ "profile_views": "ดูข้อมูลส่วนตัว", "reputation": "ชื่อเสียง", "favourites": "ชอบ", - "watched": "Watched", + "watched": "ดูแล้ว", "followers": "คนติดตาม", "following": "ติดตาม", "signature": "ลายเซ็น", @@ -59,12 +59,12 @@ "digest_weekly": "รายสัปดาห์", "digest_monthly": "รายเดือน", "send_chat_notifications": "ส่งอีเมลเมื่อมีข้อความใหม่เข้ามาขณะที่ฉันไม่ได้ออนไลน์", - "send_post_notifications": "Send an email when replies are made to topics I am subscribed to", + "send_post_notifications": "ส่งอีเมลให้ฉันเมื่อมีการตอบกลับในหัวข้อที่ฉันเคยบอกรับเป็นสมาชิกไว้", "has_no_follower": "ผู้ใช้รายนี้ไม่มีใครติดตาม :(", "follows_no_one": "ผู้ใช้รายนี้ไม่ติดตามใคร :(", "has_no_posts": "ผู้ใช้รายนี้ไม่ได้โพสต์อะไรเลย", "has_no_topics": "สมาชิกรายนี้ยังไม่ได้มีการโพสต์ข้อความ", - "has_no_watched_topics": "This user didn't watch any topics yet.", + "has_no_watched_topics": "ผู้ใช้นี้ยังไม่เคยเข้าชมในหัวข้อใดๆ", "email_hidden": "ซ่อนอีเมล์", "hidden": "ซ่อน", "paginate_description": "ให้เลขหน้ากระทู้และโพสต์แทนการใช้สกรอลล์ที่ไม่สิ้นสุด", @@ -73,8 +73,8 @@ "notification_sounds": "เตือนด้วยเสียงเมื่อมีข้อความแจ้งเตือน", "browsing": "เปิดดูการตั้งค่า", "open_links_in_new_tab": "เปิดลิงค์ในแท็บใหม่", - "enable_topic_searching": "Enable In-Topic Searching", - "topic_search_help": "If enabled, in-topic searching will override the browser's default page search behaviour and allow you to search through the entire topic, instead of what is only shown on screen.", + "enable_topic_searching": "เปิดใช้การค้นหาแบบ In-Topic", + "topic_search_help": "เมื่อการค้นหาแบบ In-Topic ถูกเปิดใช้งาน การค้นหาแบบ In-Topic จะทำงานแทนการค้นหาในรูปแบบเดิม ซึ่งช่วยให้คุณสามารถทำการค้นหาจาก Topic ทั้งหมด เพิ่มเติมจากที่คุณกำลังเห็นอยู่บนหน้าจอ", "follow_topics_you_reply_to": "ติดตามกระทู้ที่คุณตอบ", "follow_topics_you_create": "ติดตามกระทู้ที่คุณตั้ง" } \ No newline at end of file diff --git a/public/language/zh_CN/groups.json b/public/language/zh_CN/groups.json index 39eb395078..e2fc6cdf7f 100644 --- a/public/language/zh_CN/groups.json +++ b/public/language/zh_CN/groups.json @@ -4,8 +4,8 @@ "owner": "用户组组长", "new_group": "创建新用户组", "no_groups_found": "还没有用户组", - "pending.accept": "Accept", - "pending.reject": "Reject", + "pending.accept": "接受", + "pending.reject": "取消", "cover-instructions": "拖放照片,拖动位置,然后点击 保存", "cover-change": "变更", "cover-save": "保存", @@ -15,19 +15,19 @@ "details.pending": "预备成员", "details.has_no_posts": "此用户组的会员尚未发表任何帖子。", "details.latest_posts": "最新帖子", - "details.private": "Private", + "details.private": "私有", "details.grant": "授予/取消所有权", "details.kick": "踢", "details.owner_options": "用户组管理", - "details.group_name": "Group Name", + "details.group_name": "用户组名", "details.description": "Description", "details.badge_preview": "Badge Preview", - "details.change_icon": "Change Icon", + "details.change_icon": "更改图标", "details.change_colour": "Change Colour", "details.badge_text": "Badge Text", "details.userTitleEnabled": "Show Badge", "details.private_help": "If enabled, joining of groups requires approval from a group owner", - "details.hidden": "Hidden", + "details.hidden": "隐藏", "details.hidden_help": "If enabled, this group will not be found in the groups listing, and users will have to be invited manually", "event.updated": "用户组信息已更新", "event.deleted": "用户组 \"%1\" 已被删除" diff --git a/public/language/zh_CN/login.json b/public/language/zh_CN/login.json index 3e8d54af50..b6d1c9926e 100644 --- a/public/language/zh_CN/login.json +++ b/public/language/zh_CN/login.json @@ -1,7 +1,7 @@ { "username-email": "Username / Email", - "username": "Username", - "email": "Email", + "username": "用户名", + "email": "邮件", "remember_me": "记住我?", "forgot_password": "忘记密码?", "alternative_logins": "使用合作网站帐号登录", diff --git a/public/language/zh_CN/notifications.json b/public/language/zh_CN/notifications.json index f8a88ca3fe..f20ee0f1e4 100644 --- a/public/language/zh_CN/notifications.json +++ b/public/language/zh_CN/notifications.json @@ -2,7 +2,7 @@ "title": "通知", "no_notifs": "您没有新的通知", "see_all": "查看全部通知", - "mark_all_read": "Mark all notifications read", + "mark_all_read": "标记全部为已读", "back_to_home": "返回 %1", "outgoing_link": "站外链接", "outgoing_link_message": "您正在离开 %1。", diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js index 0161d8a6ae..f5cca44ccf 100644 --- a/public/src/ajaxify.js +++ b/public/src/ajaxify.js @@ -25,6 +25,8 @@ $(document).ready(function() { ajaxify.go = function (url, callback, quiet) { if (ajaxify.handleACPRedirect(url)) { return true; + } else if (ajaxify.handleNonAPIRoutes(url)) { + return true; } app.enterRoom(''); @@ -39,8 +41,6 @@ $(document).ready(function() { $('#footer, #content').removeClass('hide').addClass('ajaxifying'); - var startTime = (new Date()).getTime(); - ajaxify.variables.flush(); ajaxify.loadData(url, function(err, data) { if (err) { @@ -51,7 +51,7 @@ $(document).ready(function() { translator.load(config.defaultLang, data.template.name); - renderTemplate(url, data.template.name, data, startTime, callback); + renderTemplate(url, data.template.name, data, callback); require(['search'], function(search) { search.topicDOM.end(); @@ -64,12 +64,21 @@ $(document).ready(function() { ajaxify.handleACPRedirect = function(url) { // If ajaxifying into an admin route from regular site, do a cold load. url = ajaxify.removeRelativePath(url.replace(/\/$/, '')); - if (url.indexOf('admin') === 0 && window.location.pathname.indexOf('/admin') !== 0) { + if (url.indexOf('admin') === 0 && window.location.pathname.indexOf(RELATIVE_PATH + '/admin') !== 0) { window.open(RELATIVE_PATH + '/' + url, '_blank'); return true; } return false; - } + }; + + ajaxify.handleNonAPIRoutes = function(url) { + url = ajaxify.removeRelativePath(url.replace(/\/$/, '')); + if (url.indexOf('uploads') === 0) { + window.open(RELATIVE_PATH + '/' + url, '_blank'); + return true; + } + return false; + }; ajaxify.start = function(url, quiet, search) { url = ajaxify.removeRelativePath(url.replace(/\/$/, '')); @@ -78,6 +87,10 @@ $(document).ready(function() { $(window).trigger('action:ajaxify.start', {url: url}); + if (!window.location.pathname.match(/\/(403|404)$/g)) { + app.previousUrl = window.location.href; + } + ajaxify.currentPage = url; if (window.history && window.history.pushState) { @@ -114,25 +127,22 @@ $(document).ready(function() { } } - function renderTemplate(url, tpl_url, data, startTime, callback) { - var animationDuration = parseFloat($('#content').css('transition-duration')) || 0.2; + function renderTemplate(url, tpl_url, data, callback) { $(window).trigger('action:ajaxify.loadingTemplates', {}); templates.parse(tpl_url, data, function(template) { translator.translate(template, function(translatedTemplate) { - setTimeout(function() { - $('#content').html(translatedTemplate); + $('#content').html(translatedTemplate); - ajaxify.end(url, tpl_url); + ajaxify.end(url, tpl_url); - if (typeof callback === 'function') { - callback(); - } + if (typeof callback === 'function') { + callback(); + } - $('#content, #footer').removeClass('ajaxifying'); + $('#content, #footer').removeClass('ajaxifying'); - app.refreshTitle(url); - }, animationDuration * 1000 - ((new Date()).getTime() - startTime)); + app.refreshTitle(url); }); }); } @@ -243,10 +253,6 @@ $(document).ready(function() { return e.preventDefault(); } - if (!window.location.pathname.match(/\/(403|404)$/g)) { - app.previousUrl = window.location.href; - } - if (!e.ctrlKey && !e.shiftKey && !e.metaKey && e.which === 1) { if (this.host === '' || this.host === window.location.host) { // Internal link diff --git a/public/src/app.js b/public/src/app.js index fdfe7856b3..8974d2316a 100644 --- a/public/src/app.js +++ b/public/src/app.js @@ -234,7 +234,7 @@ app.cacheBuster = null; app.processPage = function () { highlightNavigationLink(); - $('span.timeago').timeago(); + $('.timeago').timeago(); utils.makeNumbersHumanReadable($('.human-readable-number')); diff --git a/public/src/client/account/favourites.js b/public/src/client/account/favourites.js index 6a08a814a5..f73e1d4f20 100644 --- a/public/src/client/account/favourites.js +++ b/public/src/client/account/favourites.js @@ -34,7 +34,7 @@ define('forum/account/favourites', ['forum/account/header', 'forum/infinitescrol infinitescroll.parseAndTranslate('account/favourites', 'posts', {posts: posts}, function(html) { $('.user-favourite-posts').append(html); html.find('img').addClass('img-responsive'); - html.find('span.timeago').timeago(); + html.find('.timeago').timeago(); app.createUserTooltips(); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); callback(); diff --git a/public/src/client/account/posts.js b/public/src/client/account/posts.js index 70410e0819..c4759b9016 100644 --- a/public/src/client/account/posts.js +++ b/public/src/client/account/posts.js @@ -35,7 +35,7 @@ define('forum/account/posts', ['forum/account/header', 'forum/infinitescroll'], infinitescroll.parseAndTranslate('account/posts', 'posts', {posts: posts}, function(html) { $('.user-favourite-posts').append(html); html.find('img').addClass('img-responsive'); - html.find('span.timeago').timeago(); + html.find('.timeago').timeago(); app.createUserTooltips(); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); callback(); diff --git a/public/src/client/account/profile.js b/public/src/client/account/profile.js index 89a555a702..5bbf48fe85 100644 --- a/public/src/client/account/profile.js +++ b/public/src/client/account/profile.js @@ -116,7 +116,7 @@ define('forum/account/profile', ['forum/account/header', 'forum/infinitescroll'] infinitescroll.parseAndTranslate('account/profile', 'posts', {posts: posts}, function(html) { $('.user-recent-posts .loading-indicator').before(html); - html.find('span.timeago').timeago(); + html.find('.timeago').timeago(); callback(); }); diff --git a/public/src/client/account/topics.js b/public/src/client/account/topics.js index c556ef2891..3d3923ceef 100644 --- a/public/src/client/account/topics.js +++ b/public/src/client/account/topics.js @@ -32,8 +32,8 @@ define('forum/account/topics', ['forum/account/header', 'forum/infinitescroll'], function onTopicsLoaded(topics, callback) { infinitescroll.parseAndTranslate('account/topics', 'topics', {topics: topics}, function(html) { - $('#topics-container').append(html); - html.find('span.timeago').timeago(); + $('[component="category"]').append(html); + html.find('.timeago').timeago(); app.createUserTooltips(); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); $(window).trigger('action:topics.loaded'); diff --git a/public/src/client/account/watched.js b/public/src/client/account/watched.js index 9a502b1ee8..c9d4bed788 100644 --- a/public/src/client/account/watched.js +++ b/public/src/client/account/watched.js @@ -30,8 +30,8 @@ define('forum/account/watched', ['forum/account/header', 'forum/infinitescroll'] function onTopicsLoaded(topics, callback) { infinitescroll.parseAndTranslate('account/watched', 'topics', {topics: topics}, function(html) { - $('#topics-container').append(html); - html.find('span.timeago').timeago(); + $('[component="category"]').append(html); + html.find('.timeago').timeago(); app.createUserTooltips(); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); $(window).trigger('action:topics.loaded'); diff --git a/public/src/client/categories.js b/public/src/client/categories.js index d3cf1cae43..e3882ff831 100644 --- a/public/src/client/categories.js +++ b/public/src/client/categories.js @@ -29,7 +29,7 @@ define('forum/categories', function() { }; function renderNewPost(cid, post) { - var category = $('.category-item[data-cid="' + cid + '"]'); + var category = components.get('category/topic', 'cid', cid); if (!category.length) { return; } @@ -67,7 +67,7 @@ define('forum/categories', function() { translator.translate(html, function(translatedHTML) { translatedHTML = $(translatedHTML); translatedHTML.find('img').addClass('img-responsive'); - translatedHTML.find('span.timeago').timeago(); + translatedHTML.find('.timeago').timeago(); callback(translatedHTML); }); }); diff --git a/public/src/client/category.js b/public/src/client/category.js index 3b6a0e69b9..1e6c0e94c4 100644 --- a/public/src/client/category.js +++ b/public/src/client/category.js @@ -20,12 +20,6 @@ define('forum/category', [ } }); - $(window).on('action:composer.topics.post', function(ev, data) { - localStorage.removeItem('category:' + data.data.cid + ':bookmark'); - localStorage.removeItem('category:' + data.data.cid + ':bookmark:clicked'); - ajaxify.go('topic/' + data.data.slug); - }); - function removeListeners() { socket.removeListener('event:new_topic', Category.onNewTopic); categoryTools.removeListeners(); @@ -52,12 +46,12 @@ define('forum/category', [ enableInfiniteLoadingOrPagination(); if (!config.usePagination) { - navigator.init('#topics-container > .category-item', ajaxify.variables.get('topic_count'), Category.toTop, Category.toBottom, Category.navigatorCallback); + navigator.init('[component="category/topic"]', ajaxify.variables.get('topic_count'), Category.toTop, Category.toBottom, Category.navigatorCallback); } - $('#topics-container').on('click', '.topic-title', function() { + $('[component="category"]').on('click', '[component="post/header"]', function() { var clickedIndex = $(this).parents('[data-index]').attr('data-index'); - $('#topics-container li.category-item').each(function(index, el) { + $('[component="category/topic"]').each(function(index, el) { if ($(el).offset().top - $(window).scrollTop() > 0) { localStorage.setItem('category:' + cid + ':bookmark', $(el).attr('data-index')); localStorage.setItem('category:' + cid + ':bookmark:clicked', clickedIndex); @@ -95,8 +89,8 @@ define('forum/category', [ }); }; - Category.navigatorCallback = function(element, elementCount) { - return parseInt(element.attr('data-index'), 10) + 1; + Category.navigatorCallback = function(topIndex, bottomIndex, elementCount) { + return bottomIndex; }; $(window).on('action:popstate', function(ev, data) { @@ -135,7 +129,7 @@ define('forum/category', [ bookmarkIndex = 0; } - $('#topics-container').empty(); + $('[component="category"]').empty(); loadTopicsAfter(bookmarkIndex, function() { Category.scrollToTopic(bookmarkIndex, clickedIndex, 0); @@ -145,7 +139,7 @@ define('forum/category', [ }); Category.highlightTopic = function(topicIndex) { - var highlight = $('#topics-container [data-index="' + topicIndex + '"]'); + var highlight = components.get('category/topic', 'index', topicIndex); if (highlight.length && !highlight.hasClass('highlight')) { highlight.addClass('highlight'); setTimeout(function() { @@ -163,7 +157,7 @@ define('forum/category', [ offset = 0; } - var scrollTo = $('#topics-container [data-index="' + bookmarkIndex + '"]'); + var scrollTo = components.get('category/topic', 'index', bookmarkIndex); var cid = ajaxify.variables.get('category_id'); if (scrollTo.length && cid) { $('html, body').animate({ @@ -198,11 +192,12 @@ define('forum/category', [ }, function(html) { translator.translate(html, function(translatedHTML) { var topic = $(translatedHTML), - container = $('#topics-container'), - topics = $('#topics-container').children('.category-item'), + container = $('[component="category"]'), + topics = $('[component="category/topic"]'), numTopics = topics.length; - $('#topics-container, .category-sidebar').removeClass('hidden'); + $('[component="category"]').removeClass('hidden'); + $('.category-sidebar').removeClass('hidden'); var noTopicsWarning = $('#category-no-topics'); if (noTopicsWarning.length) { @@ -228,7 +223,7 @@ define('forum/category', [ topic.hide().fadeIn('slow'); - topic.find('span.timeago').timeago(); + topic.find('.timeago').timeago(); app.createUserTooltips(); updateTopicCount(); @@ -253,7 +248,7 @@ define('forum/category', [ function removeAlreadyAddedTopics(topics) { return topics.filter(function(topic) { - return $('#topics-container li[data-tid="' + topic.tid +'"]').length === 0; + return components.get('category/topic', 'tid', topic.tid).length === 0; }); } @@ -261,16 +256,20 @@ define('forum/category', [ before = null; function findInsertionPoint() { - if (!$('#topics-container .category-item[data-tid]').length) { + var topics = components.get('category/topic'); + + if (!topics.length) { return; } - var last = $('#topics-container .category-item[data-tid]').last(); - var lastIndex = last.attr('data-index'); - var firstIndex = data.topics[data.topics.length - 1].index; + + var last = topics.last(), + lastIndex = last.attr('data-index'), + firstIndex = data.topics[data.topics.length - 1].index; + if (firstIndex > lastIndex) { after = last; } else { - before = $('#topics-container .category-item[data-tid]').first(); + before = topics.first(); } } @@ -283,10 +282,12 @@ define('forum/category', [ templates.parse('category', 'topics', data, function(html) { translator.translate(html, function(translatedHTML) { - var container = $('#topics-container'), + var container = $('[component="category"]'), html = $(translatedHTML); - $('#topics-container, .category-sidebar').removeClass('hidden'); + $('[component="category"]').removeClass('hidden'); + $('.category-sidebar').removeClass('hidden'); + $('#category-no-topics').remove(); if(config.usePagination) { @@ -304,7 +305,7 @@ define('forum/category', [ if (typeof callback === 'function') { callback(); } - html.find('span.timeago').timeago(); + html.find('.timeago').timeago(); app.createUserTooltips(); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); }); @@ -312,11 +313,11 @@ define('forum/category', [ }; Category.loadMoreTopics = function(direction) { - if (!$('#topics-container').length || !$('#topics-container').children().length) { + if (!$('[component="category"]').length || !$('[component="category"]').children().length) { return; } - infinitescroll.calculateAfter(direction, '#topics-container .category-item[data-tid]', config.topicsPerPage, false, function(after, offset, el) { + infinitescroll.calculateAfter(direction, components.get('category/topic'), config.topicsPerPage, false, function(after, offset, el) { loadTopicsAfter(after, function() { if (direction < 0 && el) { Category.scrollToTopic(el.attr('data-index'), null, 0, offset); @@ -326,7 +327,7 @@ define('forum/category', [ }; function loadTopicsAfter(after, callback) { - if(!utils.isNumber(after) || (after === 0 && $('#topics-container li.category-item[data-index="0"]').length)) { + if(!utils.isNumber(after) || (after === 0 && components.get('category/topic', 'index', 0).length)) { return; } @@ -342,7 +343,7 @@ define('forum/category', [ done(); callback(); }); - $('#topics-container').attr('data-nextstart', data.nextStart); + $('[component="category"]').attr('data-nextstart', data.nextStart); } else { done(); } diff --git a/public/src/client/categoryTools.js b/public/src/client/categoryTools.js index 0e9bae608b..e711bce436 100644 --- a/public/src/client/categoryTools.js +++ b/public/src/client/categoryTools.js @@ -173,26 +173,26 @@ define('forum/categoryTools', ['forum/topic/move', 'topicSelect'], function(move } function getTopicEl(tid) { - return $('#topics-container li[data-tid="' + tid + '"]'); + return components.get('category/topic', 'tid', tid); } function setDeleteState(data) { var topic = getTopicEl(data.tid); topic.toggleClass('deleted', data.isDeleted); - topic.find('.fa-lock').toggleClass('hide', !data.isDeleted); + topic.find('[component="topic/locked"]').toggleClass('hide', !data.isDeleted); } function setPinnedState(data) { var topic = getTopicEl(data.tid); topic.toggleClass('pinned', data.isPinned); - topic.find('.fa-thumb-tack').toggleClass('hide', !data.isPinned); + topic.find('[component="topic/pinned"]').toggleClass('hide', !data.isPinned); ajaxify.refresh(); } function setLockedState(data) { var topic = getTopicEl(data.tid); topic.toggleClass('locked', data.isLocked); - topic.find('.fa-lock').toggleClass('hide', !data.isLocked); + topic.find('[component="topic/locked"]').toggleClass('hide', !data.isLocked); } function onTopicMoved(data) { diff --git a/public/src/client/chats.js b/public/src/client/chats.js index fe491b49c3..9ea8beea57 100644 --- a/public/src/client/chats.js +++ b/public/src/client/chats.js @@ -20,10 +20,8 @@ define('forum/chats', ['string', 'sounds', 'forum/infinitescroll'], function(S, Chats.addEventListeners(); Chats.setActive(); - $(window).on('action:ajaxify.end', function() { - Chats.resizeMainWindow(); - Chats.scrollToBottom(containerEl); - }); + Chats.resizeMainWindow(); + Chats.scrollToBottom($('.expanded-chat ul')); Chats.initialised = true; }; @@ -124,7 +122,7 @@ define('forum/chats', ['string', 'sounds', 'forum/infinitescroll'], function(S, function onMessagesParsed(html) { var newMessage = $(html); newMessage.insertBefore($('.user-typing')); - newMessage.find('span.timeago').timeago(); + newMessage.find('.timeago').timeago(); newMessage.find('img:not(".chat-user-image")').addClass('img-responsive'); Chats.scrollToBottom($('.expanded-chat .chat-content')); } diff --git a/public/src/client/notifications.js b/public/src/client/notifications.js index fb778ec18f..4e7244f83f 100644 --- a/public/src/client/notifications.js +++ b/public/src/client/notifications.js @@ -16,7 +16,7 @@ define('forum/notifications', function() { }); }); - $('span.timeago').timeago(); + $('.timeago').timeago(); $('.notifications .delete').on('click', function() { socket.emit('notifications.markAllRead', function(err) { diff --git a/public/src/client/recent.js b/public/src/client/recent.js index 05e936a577..30d635ea0d 100644 --- a/public/src/client/recent.js +++ b/public/src/client/recent.js @@ -94,17 +94,17 @@ define('forum/recent', ['forum/infinitescroll', 'composer'], function(infinitesc }; Recent.loadMoreTopics = function(direction) { - if(direction < 0 || !$('#topics-container').length) { + if(direction < 0 || !$('[component="category"]').length) { return; } infinitescroll.loadMore('topics.loadMoreFromSet', { - after: $('#topics-container').attr('data-nextstart'), + after: $('[component="category"]').attr('data-nextstart'), set: 'topics:recent' }, function(data, done) { if (data.topics && data.topics.length) { Recent.onTopicsLoaded('recent', data.topics, false, done); - $('#topics-container').attr('data-nextstart', data.nextStart); + $('[component="category"]').attr('data-nextstart', data.nextStart); } else { done(); } @@ -114,7 +114,7 @@ define('forum/recent', ['forum/infinitescroll', 'composer'], function(infinitesc Recent.onTopicsLoaded = function(templateName, topics, showSelect, callback) { topics = topics.filter(function(topic) { - return !$('#topics-container li[data-tid=' + topic.tid + ']').length; + return !components.get('category/topic', 'tid', topic.tid).length; }); if (!topics.length) { @@ -124,8 +124,8 @@ define('forum/recent', ['forum/infinitescroll', 'composer'], function(infinitesc infinitescroll.parseAndTranslate(templateName, 'topics', {topics: topics, showSelect: showSelect}, function(html) { $('#category-no-topics').remove(); - $('#topics-container').append(html); - html.find('span.timeago').timeago(); + $('[component="category"]').append(html); + html.find('.timeago').timeago(); app.createUserTooltips(); utils.makeNumbersHumanReadable(html.find('.human-readable-number')); $(window).trigger('action:topics.loaded'); diff --git a/public/src/client/reset.js b/public/src/client/reset.js index 7e272c3284..568d0739a1 100644 --- a/public/src/client/reset.js +++ b/public/src/client/reset.js @@ -24,6 +24,7 @@ define('forum/reset', function() { successEl.addClass('hide').hide(); errorEl.removeClass('hide').show(); } + return false; }); }; diff --git a/public/src/client/reset_code.js b/public/src/client/reset_code.js index c0f46dc356..846feffb42 100644 --- a/public/src/client/reset_code.js +++ b/public/src/client/reset_code.js @@ -31,22 +31,8 @@ define('forum/reset_code', function() { window.location.href = RELATIVE_PATH + '/login'; }); } + return false; }); - - // socket.emit('user.reset.valid', reset_code, function(err, valid) { - // if(err) { - // return app.alertError(err.message); - // } - - // if (valid) { - // resetEl.prop('disabled', false); - // } else { - // var formEl = $('#reset-form'); - // // Show error message - // $('#error').show(); - // formEl.remove(); - // } - // }); }; return ResetCode; diff --git a/public/src/client/tag.js b/public/src/client/tag.js index 3565c7e151..7328934e2d 100644 --- a/public/src/client/tag.js +++ b/public/src/client/tag.js @@ -8,7 +8,7 @@ define('forum/tag', ['forum/recent', 'forum/infinitescroll'], function(recent, i Tag.init = function() { app.enterRoom('tags'); - if ($('body').height() <= $(window).height() && $('#topics-container').children().length >= 20) { + if ($('body').height() <= $(window).height() && $('[component="category"]').children().length >= 20) { $('#load-more-btn').show(); } @@ -19,17 +19,17 @@ define('forum/tag', ['forum/recent', 'forum/infinitescroll'], function(recent, i infinitescroll.init(loadMoreTopics); function loadMoreTopics(direction) { - if(direction < 0 || !$('#topics-container').length) { + if(direction < 0 || !$('[component="category"]').length) { return; } infinitescroll.loadMore('topics.loadMoreFromSet', { set: 'tag:' + ajaxify.variables.get('tag') + ':topics', - after: $('#topics-container').attr('data-nextstart') + after: $('[component="category"]').attr('data-nextstart') }, function(data, done) { if (data.topics && data.topics.length) { recent.onTopicsLoaded('tag', data.topics, false, done); - $('#topics-container').attr('data-nextstart', data.nextStart); + $('[component="category"]').attr('data-nextstart', data.nextStart); } else { done(); $('#load-more-btn').hide(); diff --git a/public/src/client/topic.js b/public/src/client/topic.js index 9acd1a4d73..cbf4074d4f 100644 --- a/public/src/client/topic.js +++ b/public/src/client/topic.js @@ -1,7 +1,7 @@ 'use strict'; -/* globals define, app, templates, translator, socket, bootbox, config, ajaxify, RELATIVE_PATH, utils */ +/* globals define, app, components, templates, translator, socket, bootbox, config, ajaxify, RELATIVE_PATH, utils */ define('forum/topic', [ 'forum/pagination', @@ -20,7 +20,7 @@ define('forum/topic', [ $(window).on('action:ajaxify.start', function(ev, data) { if (ajaxify.currentPage !== data.url) { navigator.hide(); - $('.header-topic-title').find('span').text('').hide(); + components.get('navbar/title').find('span').text('').hide(); app.removeAlert('bookmark'); events.removeListeners(); @@ -53,7 +53,7 @@ define('forum/topic', [ handleBookmark(tid); - navigator.init('.posts > .post-row', ajaxify.variables.get('postcount'), Topic.toTop, Topic.toBottom, Topic.navigatorCallback, Topic.calculateIndex); + navigator.init(components.get('post'), ajaxify.variables.get('postcount'), Topic.toTop, Topic.toBottom, Topic.navigatorCallback, Topic.calculateIndex); $(window).on('scroll', updateTopicTitle); @@ -112,7 +112,7 @@ define('forum/topic', [ } function addBlockQuoteHandler() { - $('#post-container').on('click', 'blockquote .toggle', function() { + components.get('topic').on('click', 'blockquote .toggle', function() { var blockQuote = $(this).parent('blockquote'); var toggle = $(this); blockQuote.toggleClass('uncollapsed'); @@ -124,7 +124,7 @@ define('forum/topic', [ function enableInfiniteLoadingOrPagination() { if(!config.usePagination) { - infinitescroll.init(posts.loadMorePosts, $('#post-container .post-row[data-index="0"]').height()); + infinitescroll.init(posts.loadMorePosts, components.get('post', 'index', 0).height()); } else { navigator.hide(); @@ -135,9 +135,9 @@ define('forum/topic', [ function updateTopicTitle() { if($(window).scrollTop() > 50) { - $('.header-topic-title').find('span').text(ajaxify.variables.get('topic_name')).show(); + components.get('navbar/title').find('span').text(ajaxify.variables.get('topic_name')).show(); } else { - $('.header-topic-title').find('span').text('').hide(); + components.get('navbar/title').find('span').text('').hide(); } } @@ -148,18 +148,18 @@ define('forum/topic', [ return index; }; - Topic.navigatorCallback = function(element, elementCount) { + Topic.navigatorCallback = function(topPostIndex, bottomPostIndex, elementCount) { var path = ajaxify.removeRelativePath(window.location.pathname.slice(1)); if (!path.startsWith('topic')) { return 1; } - var postIndex = parseInt(element.attr('data-index'), 10); - var index = postIndex + 1; + var postIndex = topPostIndex; + var index = bottomPostIndex; if (config.topicPostSort !== 'oldest_to_newest') { - if (postIndex === 0) { + if (bottomPostIndex === 0) { index = 1; } else { - index = Math.max(elementCount - postIndex + 1, 1); + index = Math.max(elementCount - bottomPostIndex + 2, 1); } } @@ -175,8 +175,8 @@ define('forum/topic', [ var topicId = parts[1], slug = parts[2]; var newUrl = 'topic/' + topicId + '/' + (slug ? slug : ''); - if (postIndex > 0) { - newUrl += '/' + (postIndex + 1); + if (postIndex > 1) { + newUrl += '/' + postIndex; } if (newUrl !== currentUrl) { diff --git a/public/src/client/topic/browsing.js b/public/src/client/topic/browsing.js index 935439fa88..7c14fab158 100644 --- a/public/src/client/topic/browsing.js +++ b/public/src/client/topic/browsing.js @@ -10,7 +10,7 @@ define('forum/topic/browsing', function() { Browsing.onUpdateUsersInRoom = function(data) { if (data && data.room.indexOf('topic_' + ajaxify.variables.get('topic_id')) !== -1) { - $('.browsing-users').toggleClass('hidden', !data.users.length); + $('[component="topic/browsing/list"]').parent().toggleClass('hidden', !data.users.length); for(var i=0; i'); } } @@ -99,11 +99,11 @@ define('forum/topic/browsing', function() { if (!count || count < 0) { count = 0; } - $('.user-count').text(count).parent().toggleClass('hidden', count === 0); + $('[component="topic/browsing/count"]').text(count).parent().toggleClass('hidden', count === 0); } function increaseUserCount(incr) { - updateUserCount(parseInt($('.user-count').first().text(), 10) + incr); + updateUserCount(parseInt($('[component="topic/browsing/count"]').first().text(), 10) + incr); } return Browsing; diff --git a/public/src/client/topic/events.js b/public/src/client/topic/events.js index c90dd149b5..c3ecf6d29a 100644 --- a/public/src/client/topic/events.js +++ b/public/src/client/topic/events.js @@ -1,7 +1,7 @@ 'use strict'; -/* globals app, ajaxify, define, socket, translator, templates */ +/* globals app, ajaxify, components, define, socket, translator, templates */ define('forum/topic/events', [ 'forum/topic/browsing', @@ -69,7 +69,7 @@ define('forum/topic/events', [ }; function updatePostVotesAndUserReputation(data) { - var votes = $('[data-pid="' + data.post.pid + '"] .votes'), + var votes = components.get('post/vote-count', data.post.pid), reputationElements = $('.reputation[data-uid="' + data.post.uid + '"]'); votes.html(data.post.votes).attr('data-votes', data.post.votes); @@ -96,12 +96,12 @@ define('forum/topic/events', [ } function onPostEdited(data) { - var editedPostEl = $('#content_' + data.pid), - editedPostTitle = $('#topic_title_' + data.pid); + var editedPostEl = components.get('post/content', data.pid), + editedPostHeader = components.get('post/header', data.pid); - if (editedPostTitle.length) { - editedPostTitle.fadeOut(250, function() { - editedPostTitle.html(data.title).fadeIn(250); + if (editedPostHeader.length) { + editedPostHeader.fadeOut(250, function() { + editedPostHeader.html(data.title).fadeIn(250); }); } @@ -139,14 +139,15 @@ define('forum/topic/events', [ } function onPostPurged(pid) { - $('#post-container [data-pid="' + pid + '"]').fadeOut(500, function() { + components.get('post', 'pid', pid).fadeOut(500, function() { $(this).remove(); }); + postTools.updatePostCount(); } function togglePostDeleteState(data) { - var postEl = $('#post-container [data-pid="' + data.pid + '"]'); + var postEl = components.get('post', 'pid', data.pid); if (!postEl.length) { return; @@ -158,9 +159,9 @@ define('forum/topic/events', [ if (!app.user.isAdmin && parseInt(data.uid, 10) !== parseInt(app.user.uid, 10)) { if (isDeleted) { - postEl.find('.post-content').translateHtml('[[topic:post_is_deleted]]'); + postEl.find('[component="post/content"]').translateHtml('[[topic:post_is_deleted]]'); } else { - postEl.find('.post-content').html(data.content); + postEl.find('[component="post/content"]').html(data.content); } } } diff --git a/public/src/client/topic/fork.js b/public/src/client/topic/fork.js index 216cc63f9f..91ec8a2be9 100644 --- a/public/src/client/topic/fork.js +++ b/public/src/client/topic/fork.js @@ -1,6 +1,6 @@ 'use strict'; -/* globals define, app, ajaxify, translator, socket */ +/* globals define, app, ajaxify, components, translator, socket */ define('forum/topic/fork', function() { @@ -10,7 +10,7 @@ define('forum/topic/fork', function() { pids = []; Fork.init = function() { - $('.fork_thread').on('click', onForkThreadClicked); + components.get('topic/fork').on('click', onForkThreadClicked); }; function disableClicks() { @@ -18,11 +18,11 @@ define('forum/topic/fork', function() { } function disableClicksOnPosts() { - $('.post-row').on('click', 'button,a', disableClicks); + components.get('post').on('click', 'button,a', disableClicks); } function enableClicksOnPosts() { - $('.post-row').off('click', 'button,a', disableClicks); + components.get('post').off('click', 'button,a', disableClicks); } function onForkThreadClicked() { @@ -35,7 +35,7 @@ define('forum/topic/fork', function() { forkModal.find('.close,#fork_thread_cancel').on('click', closeForkModal); forkModal.find('#fork-title').on('change', checkForkButtonEnable); - $('#post-container').on('click', '[data-pid]', function() { + components.get('topic').on('click', '[data-pid]', function() { togglePostSelection($(this)); }); @@ -58,7 +58,7 @@ define('forum/topic/fork', function() { pids: pids }, function(err, newTopic) { function fadeOutAndRemove(pid) { - $('#post-container [data-pid="' + pid + '"]').fadeOut(500, function() { + components.get('post', 'pid', pid).fadeOut(500, function() { $(this).remove(); }); } @@ -125,10 +125,11 @@ define('forum/topic/fork', function() { function closeForkModal() { for(var i=0; i 0) { + if ($(selection.baseNode).parents('[component="post/content"]').length > 0) { var snippet = selection.toString(); if (snippet.length) { selectionText = '> ' + snippet.replace(/\n/g, '\n> ') + '\n\n'; @@ -185,7 +197,7 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com } function toggleVote(button, className, method) { - var post = button.parents('.post-row'), + var post = button.parents('[data-pid]'), currentState = post.find(className).length; socket.emit(currentState ? 'posts.unvote' : method , { @@ -222,7 +234,7 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com } function getData(button, data) { - return button.parents('.post-row').attr(data); + return button.parents('[data-pid]').attr(data); } function getUserName(button) { @@ -241,7 +253,7 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com function deletePost(button, tid) { var pid = getData(button, 'data-pid'), - postEl = $('#post-container li[data-pid="' + pid + '"]'), + postEl = components.get('post', 'pid', pid), action = !postEl.hasClass('deleted') ? 'delete' : 'restore'; postAction(action, pid, tid); @@ -290,7 +302,7 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com }); moveBtn.on('click', function() { - movePost(button.parents('.post-row'), getData(button, 'data-pid'), topicId.val()); + movePost(button.parents('[data-pid]'), getData(button, 'data-pid'), topicId.val()); }); } @@ -338,7 +350,7 @@ define('forum/topic/postTools', ['composer', 'share', 'navigator'], function(com } function openChat(button) { - var post = button.parents('li.post-row'); + var post = button.parents('data-pid'); app.openChat(post.attr('data-username'), post.attr('data-uid')); button.parents('.btn-group').find('.dropdown-toggle').click(); diff --git a/public/src/client/topic/posts.js b/public/src/client/topic/posts.js index 8731121de1..c2ea3940ff 100644 --- a/public/src/client/topic/posts.js +++ b/public/src/client/topic/posts.js @@ -1,6 +1,6 @@ 'use strict'; -/* globals config, app, ajaxify, define, socket, utils */ +/* globals config, app, ajaxify, components, define, socket, utils */ define('forum/topic/posts', [ 'forum/pagination', @@ -22,11 +22,11 @@ define('forum/topic/posts', [ } for (var i=0; i'); } }); postTools.updatePostCount(); - addBlockquoteEllipses(element.find('.post-content > blockquote')); + addBlockquoteEllipses(element.find('[component="post/content"] > blockquote')); hidePostToolsForDeletedPosts(element); showBottomPostBar(); }; function showBottomPostBar() { - if($('#post-container .post-row').length > 1 || !$('#post-container [data-index="0"]').length) { + if(components.get('post').length > 1 || !components.get('post', 'index', 0).length) { $('.bottom-post-bar').removeClass('hide'); } } function hidePostToolsForDeletedPosts(element) { - element.find('.post-row.deleted').each(function() { + element.find('[data-pid].deleted').each(function() { postTools.toggle($(this).attr('data-pid'), true); }); } diff --git a/public/src/client/topic/threadTools.js b/public/src/client/topic/threadTools.js index c3923e2720..0d10cd844c 100644 --- a/public/src/client/topic/threadTools.js +++ b/public/src/client/topic/threadTools.js @@ -1,6 +1,6 @@ 'use strict'; -/* globals define, app, translator, ajaxify, socket, bootbox */ +/* globals define, app, components, translator, ajaxify, socket, bootbox */ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move'], function(fork, move) { @@ -21,27 +21,27 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move'], func ThreadTools.setPinnedState({tid: tid, isPinned: true}); } - $('.delete_thread').on('click', function() { + components.get('topic/delete').on('click', function() { topicCommand(threadState.deleted ? 'restore' : 'delete', tid); return false; }); - $('.purge_thread').on('click', function() { + components.get('topic/purge').on('click', function() { topicCommand('purge', tid); return false; }); - $('.lock_thread').on('click', function() { + components.get('topic/lock').on('click', function() { socket.emit(threadState.locked ? 'topics.unlock' : 'topics.lock', {tids: [tid], cid: ajaxify.variables.get('category_id')}); return false; }); - $('.pin_thread').on('click', function() { + components.get('topic/pin').on('click', function() { socket.emit(threadState.pinned ? 'topics.unpin' : 'topics.pin', {tids: [tid], cid: ajaxify.variables.get('category_id')}); return false; }); - $('.markAsUnreadForAll').on('click', function() { + components.get('topic/mark-unread-for-all').on('click', function() { var btn = $(this); socket.emit('topics.markAsUnreadForAll', [tid], function(err) { if(err) { @@ -53,14 +53,14 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move'], func return false; }); - $('.move_thread').on('click', function(e) { + components.get('topic/move').on('click', function(e) { move.init([tid], ajaxify.variables.get('category_id')); return false; }); fork.init(); - $('.posts').on('click', '.follow', function() { + components.get('topic').on('click', '[component="topic/follow"]', function() { socket.emit('topics.toggleFollow', tid, function(err, state) { if(err) { return app.alert({ @@ -97,35 +97,36 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move'], func } ThreadTools.setLockedState = function(data) { - var threadEl = $('#post-container'); + var threadEl = components.get('topic'); if (parseInt(data.tid, 10) === parseInt(threadEl.attr('data-tid'), 10)) { var isLocked = data.isLocked && !app.user.isAdmin; - $('.lock_thread').translateHtml(' [[topic:thread_tools.' + (data.isLocked ? 'un': '') + 'lock]]'); + components.get('topic/lock').translateHtml(' [[topic:thread_tools.' + (data.isLocked ? 'un': '') + 'lock]]'); translator.translate(isLocked ? '[[topic:locked]]' : '[[topic:reply]]', function(translated) { var className = isLocked ? 'fa-lock' : 'fa-reply'; - threadEl.find('.post_reply').html(' ' + translated); - $('.topic-main-buttons .post_reply').attr('disabled', isLocked).html(isLocked ? ' ' + translated : translated); + threadEl.find('[component="post/reply"]').html(' ' + translated).attr('disabled', isLocked); + $('[component="topic/reply"]').attr('disabled', isLocked).html(isLocked ? ' ' + translated : translated); }); - threadEl.find('.quote, .edit, .delete').toggleClass('hidden', isLocked); - $('.topic-title i.fa-lock').toggleClass('hide', !data.isLocked); + threadEl.find('[component="post/quote"], [component="post/edit"], [component="post/delete"]').toggleClass('hidden', isLocked); + $('[component="post/header"] i.fa-lock').toggleClass('hide', !data.isLocked); ThreadTools.threadState.locked = data.isLocked; } }; ThreadTools.setDeleteState = function(data) { - var threadEl = $('#post-container'); + var threadEl = components.get('topic'); if (parseInt(data.tid, 10) !== parseInt(threadEl.attr('data-tid'), 10)) { return; } - $('.delete_thread span').translateHtml(' [[topic:thread_tools.' + (data.isDelete ? 'restore' : 'delete') + ']]'); + components.get('topic/delete').translateHtml(' [[topic:thread_tools.' + (data.isDelete ? 'restore' : 'delete') + ']]'); threadEl.toggleClass('deleted', data.isDelete); ThreadTools.threadState.deleted = data.isDelete; - $('.purge_thread').toggleClass('hidden', !data.isDelete); + + components.get('topic/purge').toggleClass('hidden', !data.isDelete); if (data.isDelete) { translator.translate('[[topic:deleted_message]]', function(translated) { @@ -137,13 +138,13 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move'], func }; ThreadTools.setPinnedState = function(data) { - var threadEl = $('#post-container'); + var threadEl = components.get('topic'); if (parseInt(data.tid, 10) === parseInt(threadEl.attr('data-tid'), 10)) { translator.translate(' [[topic:thread_tools.' + (data.isPinned ? 'unpin' : 'pin') + ']]', function(translated) { - $('.pin_thread').html(translated); + components.get('topic/pin').html(translated); ThreadTools.threadState.pinned = data.isPinned; }); - $('.topic-title i.fa-thumb-tack').toggleClass('hide', !data.isPinned); + $('[component="post/header"] i.fa-thumb-tack').toggleClass('hide', !data.isPinned); } }; @@ -152,7 +153,7 @@ define('forum/topic/threadTools', ['forum/topic/fork', 'forum/topic/move'], func var iconClass = state ? 'fa fa-eye-slash' : 'fa fa-eye'; var text = state ? '[[topic:unwatch]]' : '[[topic:watch]]'; - var followEl = $('.posts .follow'); + var followEl = components.get('topic/follow'); translator.translate(title, function(titleTranslated) { followEl.attr('title', titleTranslated).find('i').attr('class', iconClass); diff --git a/public/src/client/unread.js b/public/src/client/unread.js index 7e08037f5a..c8c1c96de9 100644 --- a/public/src/client/unread.js +++ b/public/src/client/unread.js @@ -42,7 +42,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], app.alertSuccess('[[unread:topics_marked_as_read.success]]'); - $('#topics-container').empty(); + $('[component="category"]').empty(); $('#category-no-topics').removeClass('hidden'); $('.markread').addClass('hidden'); }); @@ -51,7 +51,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], $('.markread').on('click', '.category', function() { function getCategoryTids(cid) { var tids = []; - $('#topics-container .category-item[data-cid="' + cid + '"]').each(function() { + components.get('category/topic', 'cid', cid).each(function() { tids.push($(this).attr('data-tid')); }); return tids; @@ -72,7 +72,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], topicSelect.init(); - if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20) { + if ($("body").height() <= $(window).height() && $('[component="category"]').children().length >= 20) { $('#load-more-btn').show(); } @@ -83,16 +83,16 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], infinitescroll.init(loadMoreTopics); function loadMoreTopics(direction) { - if(direction < 0 || !$('#topics-container').length) { + if(direction < 0 || !$('[component="category"]').length) { return; } infinitescroll.loadMore('topics.loadMoreUnreadTopics', { - after: $('#topics-container').attr('data-nextstart') + after: $('[component="category"]').attr('data-nextstart') }, function(data, done) { if (data.topics && data.topics.length) { recent.onTopicsLoaded('unread', data.topics, true, done); - $('#topics-container').attr('data-nextstart', data.nextStart); + $('[component="category"]').attr('data-nextstart', data.nextStart); } else { done(); $('#load-more-btn').hide(); @@ -106,7 +106,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], app.alertSuccess('[[unread:topics_marked_as_read.success]]'); - if (!$('#topics-container').children().length) { + if (!$('[component="category"]').children().length) { $('#category-no-topics').removeClass('hidden'); $('.markread').addClass('hidden'); } @@ -114,7 +114,7 @@ define('forum/unread', ['forum/recent', 'topicSelect', 'forum/infinitescroll'], function removeTids(tids) { for(var i=0; i 0) { + action = 'topics.post'; composerData = { handle: handleEl ? handleEl.val() : undefined, title: titleEl.val(), content: bodyEl.val(), topic_thumb: thumbEl.val() || '', category_id: postData.cid, - tags: tags.getTags(post_uuid) + tags: tags.getTags(post_uuid), + lock: options.lock || false }; - - action = 'topics.post'; - socket.emit(action, composerData, done); } else if (parseInt(postData.tid, 10) > 0) { + action = 'posts.reply'; composerData = { tid: postData.tid, handle: handleEl ? handleEl.val() : undefined, content: bodyEl.val(), - toPid: postData.toPid + toPid: postData.toPid, + lock: options.lock || false }; - - action = 'posts.reply'; - socket.emit(action, composerData, done); } else if (parseInt(postData.pid, 10) > 0) { + action = 'posts.edit'; composerData = { pid: postData.pid, handle: handleEl ? handleEl.val() : undefined, @@ -501,12 +528,9 @@ define('composer', [ topic_thumb: thumbEl.val() || '', tags: tags.getTags(post_uuid) }; - - action = 'posts.edit'; - socket.emit(action, composerData, done); } - function done(err, data) { + socket.emit(action, composerData, function (err, data) { $('[data-action="post"]').removeAttr('disabled'); if (err) { if (err.message === '[[error:email-not-confirmed]]') { @@ -520,7 +544,7 @@ define('composer', [ drafts.removeDraft(postData.save_id); $(window).trigger('action:composer.' + action, {composerData: composerData, data: data}); - } + }); } function discard(post_uuid) { diff --git a/public/src/modules/helpers.js b/public/src/modules/helpers.js index 9f643d8736..5361099476 100644 --- a/public/src/modules/helpers.js +++ b/public/src/modules/helpers.js @@ -43,6 +43,46 @@ return JSON.stringify(obj).replace(/&/gm,"&").replace(//gm,">").replace(/"/g, '"'); }; + helpers.generateCategoryBackground = function(category) { + var style = []; + + if (category.backgroundImage) { + style.push('background-image: url(' + category.backgroundImage + ')'); + } + + if (category.bgColor) { + style.push('background-color: ' + category.bgColor + ';'); + } + + if (category.color) { + style.push('color: ' + category.color + ';'); + } + + return style.join(' '); + }; + + helpers.generateTopicClass = function(topic) { + var style = []; + + if (topic.locked) { + style.push('locked'); + } + + if (topic.pinned) { + style.push('pinned'); + } + + if (topic.deleted) { + style.push('deleted'); + } + + if (topic.unread) { + style.push('unread'); + } + + return style.join(' '); + }; + // Groups helpers helpers.membershipBtn = function(groupObj) { if (groupObj.isMember) { diff --git a/public/src/modules/navigator.js b/public/src/modules/navigator.js index e7f78c980f..697046bd28 100644 --- a/public/src/modules/navigator.js +++ b/public/src/modules/navigator.js @@ -1,7 +1,7 @@ 'use strict'; -/* globals app, define, ajaxify, utils, translator, config */ +/* globals app, components, define, ajaxify, utils, translator, config */ define('navigator', ['forum/pagination'], function(pagination) { @@ -90,18 +90,26 @@ define('navigator', ['forum/pagination'], function(pagination) { navigator.update = function() { toggle(!!count); - $($(navigator.selector).get().reverse()).each(function() { + var topIndex = 0; + var bottomIndex = 0; + $(navigator.selector).each(function() { var el = $(this); if (elementInView(el)) { - if (typeof navigator.callback === 'function') { - index = navigator.callback(el, count); - navigator.updateTextAndProgressBar(); + if (!topIndex) { + topIndex = parseInt(el.attr('data-index'), 10) + 1; + } else { + bottomIndex = parseInt(el.attr('data-index'), 10) + 1; } - + } else if (topIndex && bottomIndex) { return false; } }); + + if (typeof navigator.callback === 'function' && topIndex && bottomIndex) { + index = navigator.callback(topIndex, bottomIndex, count); + navigator.updateTextAndProgressBar(); + } }; navigator.updateTextAndProgressBar = function() { @@ -153,7 +161,7 @@ define('navigator', ['forum/pagination'], function(pagination) { } navigator.scrollToPost = function(postIndex, highlight, duration, offset) { - if (!utils.isNumber(postIndex) || !$('#post-container').length) { + if (!utils.isNumber(postIndex) || !components.get('topic').length) { return; } @@ -161,7 +169,7 @@ define('navigator', ['forum/pagination'], function(pagination) { duration = duration !== undefined ? duration : 400; navigator.scrollActive = true; - if($('#post_anchor_' + postIndex).length) { + if(components.get('post/anchor', postIndex).length) { return scrollToPid(postIndex, highlight, duration, offset); } @@ -188,7 +196,7 @@ define('navigator', ['forum/pagination'], function(pagination) { }; function scrollToPid(postIndex, highlight, duration, offset) { - var scrollTo = $('#post_anchor_' + postIndex); + var scrollTo = components.get('post/anchor', postIndex); if (!scrollTo) { navigator.scrollActive = false; @@ -222,7 +230,7 @@ define('navigator', ['forum/pagination'], function(pagination) { } } - if ($('#post-container').length) { + if (components.get('topic').length) { animateScroll(); } } diff --git a/public/src/modules/share.js b/public/src/modules/share.js index 3f26ebb9f8..6665a30b8c 100644 --- a/public/src/modules/share.js +++ b/public/src/modules/share.js @@ -50,7 +50,7 @@ define('share', function() { function getPostUrl(clickedElement) { var parts = window.location.pathname.split('/'); - var postIndex = parseInt(clickedElement.parents('.post-row').attr('data-index'), 10); + var postIndex = parseInt(clickedElement.parents('[data-index]').attr('data-index'), 10); return '/' + parts[1] + '/' + parts[2] + (parts[3] ? '/' + parts[3] : '') + (postIndex ? '/' + (postIndex + 1) : ''); } diff --git a/public/src/modules/topicSelect.js b/public/src/modules/topicSelect.js index 75f32f80be..3e3ae58d46 100644 --- a/public/src/modules/topicSelect.js +++ b/public/src/modules/topicSelect.js @@ -9,7 +9,7 @@ define('topicSelect', function() { var topicsContainer; TopicSelect.init = function(onSelect) { - topicsContainer = $('#topics-container'); + topicsContainer = $('[component="category"]'); topicsContainer.on('selectstart', function() { return false; }); @@ -18,7 +18,7 @@ define('topicSelect', function() { var select = $(this); if (ev.shiftKey) { - selectRange($(this).parents('.category-item').attr('data-tid')); + selectRange($(this).parents('[component="category/topic"]').attr('data-tid')); lastSelected = select; return false; } @@ -35,32 +35,32 @@ define('topicSelect', function() { function toggleSelect(select, isSelected) { select.toggleClass('fa-check-square-o', isSelected); select.toggleClass('fa-square-o', !isSelected); - select.parents('.category-item').toggleClass('selected', isSelected); + select.parents('[component="category/topic"]').toggleClass('selected', isSelected); } TopicSelect.getSelectedTids = function() { var tids = []; - topicsContainer.find('.category-item.selected').each(function() { + topicsContainer.find('[component="category/topic"].selected').each(function() { tids.push($(this).attr('data-tid')); }); return tids; }; TopicSelect.unselectAll = function() { - topicsContainer.find('.category-item.selected').removeClass('selected'); + topicsContainer.find('[component="category/topic"].selected').removeClass('selected'); topicsContainer.find('.select').toggleClass('fa-check-square-o', false).toggleClass('fa-square-o', true); }; function selectRange(clickedTid) { if(!lastSelected) { - lastSelected = $('.category-item[data-tid]').first().find('.select'); + lastSelected = $('[component="category/topic"]').first().find('.select'); } - var isClickedSelected = $('.category-item[data-tid="' + clickedTid + '"]').hasClass('selected'); + var isClickedSelected = components.get('category/topic', 'tid', clickedTid).hasClass('selected'); var clickedIndex = getIndex(clickedTid); - var lastIndex = getIndex(lastSelected.parents('.category-item[data-tid]').attr('data-tid')); + var lastIndex = getIndex(lastSelected.parents('[component="category/topic"]').attr('data-tid')); selectIndexRange(clickedIndex, lastIndex, !isClickedSelected); } @@ -72,13 +72,13 @@ define('topicSelect', function() { } for(var i=start; i<=end; ++i) { - var topic = $('.category-item[data-tid]').eq(i); + var topic = $('[component="category/topic"]').eq(i); toggleSelect(topic.find('.select'), isSelected); } } function getIndex(tid) { - return $('.category-item[data-tid="' + tid + '"]').index('#topics-container .category-item'); + return components.get('category/topic', 'tid', tid).index('[component="category/topic"]'); } return TopicSelect; diff --git a/public/src/translator.js b/public/src/translator.js index a6f4027faf..d1b85e300c 100644 --- a/public/src/translator.js +++ b/public/src/translator.js @@ -79,7 +79,7 @@ } $.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.' + languageCode + '.js').success(function() { - $('span.timeago').timeago(); + $('.timeago').timeago(); }).fail(function() { $.getScript(RELATIVE_PATH + '/vendor/jquery/timeago/locales/jquery.timeago.en.js'); }); diff --git a/public/src/widgets.js b/public/src/widgets.js index 990a42c6f0..e83353ec1b 100644 --- a/public/src/widgets.js +++ b/public/src/widgets.js @@ -71,7 +71,7 @@ var widgetAreas = $('#content [widget-area]'); widgetAreas.find('img:not(.user-img)').addClass('img-responsive'); - widgetAreas.find('span.timeago').timeago(); + widgetAreas.find('.timeago').timeago(); widgetAreas.find('img[title].teaser-pic,img[title].user-img').each(function() { $(this).tooltip({ placement: 'top', diff --git a/public/vendor/jquery/textcomplete/jquery.textcomplete.css b/public/vendor/jquery/textcomplete/jquery.textcomplete.css new file mode 100644 index 0000000000..d33f066c5a --- /dev/null +++ b/public/vendor/jquery/textcomplete/jquery.textcomplete.css @@ -0,0 +1,33 @@ +/* Sample */ + +/*.dropdown-menu { + border: 1px solid #ddd; + background-color: white; +} + +.dropdown-menu li { + border-top: 1px solid #ddd; + padding: 2px 5px; +} + +.dropdown-menu li:first-child { + border-top: none; +} + +.dropdown-menu li:hover, +.dropdown-menu .active { + background-color: rgb(110, 183, 219); +}*/ + + +/* SHOULD not modify */ + +/*.dropdown-menu { + list-style: none; + padding: 0; + margin: 0; +} + +.dropdown-menu a:hover { + cursor: pointer; +}*/ \ No newline at end of file diff --git a/public/vendor/jquery/textcomplete/jquery.textcomplete.min.js b/public/vendor/jquery/textcomplete/jquery.textcomplete.min.js new file mode 100644 index 0000000000..941ee008de --- /dev/null +++ b/public/vendor/jquery/textcomplete/jquery.textcomplete.min.js @@ -0,0 +1 @@ +/*! jquery-textcomplete - v0.4.0 - 2015-03-10 */if("undefined"==typeof jQuery)throw new Error("jQuery.textcomplete requires jQuery");+function(a){"use strict";var b=function(a){console.warn&&console.warn(a)};a.fn.textcomplete=function(c,d){var e=Array.prototype.slice.call(arguments);return this.each(function(){var f=a(this),g=f.data("textComplete");if(g||(g=new a.fn.textcomplete.Completer(this,d||{}),f.data("textComplete",g)),"string"==typeof c){if(!g)return;e.shift(),g[c].apply(g,e),"destroy"===c&&f.removeData("textComplete")}else a.each(c,function(c){a.each(["header","footer","placement","maxCount"],function(a){c[a]&&(g.option[a]=c[a],b(a+"as a strategy param is deprecated. Use option."),delete c[a])})}),g.register(a.fn.textcomplete.Strategy.parse(c))})}}(jQuery),+function(a){"use strict";function b(c,d){if(this.$el=a(c),this.id="textcomplete"+f++,this.strategies=[],this.views=[],this.option=a.extend({},b._getDefaults(),d),!this.$el.is("input[type=text]")&&!this.$el.is("textarea")&&!c.isContentEditable&&"true"!=c.contentEditable)throw new Error("textcomplete must be called on a Textarea or a ContentEditable.");if(c===document.activeElement)this.initialize();else{var e=this;this.$el.one("focus."+this.id,function(){e.initialize()})}}var c=function(a){var b,c;return function(){var d=Array.prototype.slice.call(arguments);if(b)return c=d,void 0;b=!0;var e=this;d.unshift(function f(){if(c){var d=c;c=void 0,d.unshift(f),a.apply(e,d)}else b=!1}),a.apply(this,d)}},d=function(a){return"[object String]"===Object.prototype.toString.call(a)},e=function(a){return"[object Function]"===Object.prototype.toString.call(a)},f=0;b._getDefaults=function(){return b.DEFAULTS||(b.DEFAULTS={appendTo:a("body"),zIndex:"100"}),b.DEFAULTS},a.extend(b.prototype,{id:null,option:null,strategies:null,adapter:null,dropdown:null,$el:null,initialize:function(){var b=this.$el.get(0);this.dropdown=new a.fn.textcomplete.Dropdown(b,this,this.option);var c,d;this.option.adapter?c=this.option.adapter:(d=this.$el.is("textarea")||this.$el.is("input[type=text]")?"number"==typeof b.selectionEnd?"Textarea":"IETextarea":"ContentEditable",c=a.fn.textcomplete[d]),this.adapter=new c(b,this,this.option)},destroy:function(){this.$el.off("."+this.id),this.adapter&&this.adapter.destroy(),this.dropdown&&this.dropdown.destroy(),this.$el=this.adapter=this.dropdown=null},trigger:function(a,b){this.dropdown||this.initialize(),null!=a||(a=this.adapter.getTextFromHeadToCaret());var c=this._extractSearchQuery(a);if(c.length){var d=c[1];if(b&&this._term===d)return;this._term=d,this._search.apply(this,c)}else this._term=null,this.dropdown.deactivate()},fire:function(a){var b=Array.prototype.slice.call(arguments,1);return this.$el.trigger(a,b),this},register:function(a){Array.prototype.push.apply(this.strategies,a)},select:function(a,b){this.adapter.select(a,b),this.fire("change").fire("textComplete:select",a,b),this.adapter.focus()},_clearAtNext:!0,_term:null,_extractSearchQuery:function(a){for(var b=0;b').css({display:"none",left:0,position:"absolute",zIndex:b.zIndex}).appendTo(c)),d}}),a.extend(b.prototype,{$el:null,$inputEl:null,completer:null,footer:null,header:null,id:null,maxCount:10,placement:"",shown:!1,data:[],className:"",destroy:function(){this.deactivate(),this.$el.off("."+this.id),this.$inputEl.off("."+this.id),this.clear(),this.$el=this.$inputEl=this.completer=null,delete d[this.id]},render:function(b){var c=this._buildContents(b),d=a.map(this.data,function(a){return a.value});this.data.length?(this._renderHeader(d),this._renderFooter(d),c&&(this._renderContents(c),this._activateIndexedItem()),this._setScroll()):this.shown&&this.deactivate()},setPosition:function(b){this.$el.css(this._applyPlacement(b));var b="absolute";return this.$inputEl.add(this.$inputEl.parents()).each(function(){return"absolute"===a(this).css("position")?!1:"fixed"===a(this).css("position")?(b="fixed",!1):void 0}),this.$el.css({position:b}),this},clear:function(){this.$el.html(""),this.data=[],this._index=0,this._$header=this._$footer=null},activate:function(){return this.shown||(this.clear(),this.$el.show(),this.className&&this.$el.addClass(this.className),this.completer.fire("textComplete:show"),this.shown=!0),this},deactivate:function(){return this.shown&&(this.$el.hide(),this.className&&this.$el.removeClass(this.className),this.completer.fire("textComplete:hide"),this.shown=!1),this},isUp:function(a){return 38===a.keyCode||a.ctrlKey&&80===a.keyCode},isDown:function(a){return 40===a.keyCode||a.ctrlKey&&78===a.keyCode},isEnter:function(a){var b=a.ctrlKey||a.altKey||a.metaKey||a.shiftKey;return!b&&(13===a.keyCode||9===a.keyCode||this.option.completeOnSpace===!0&&32===a.keyCode)},isPageup:function(a){return 33===a.keyCode},isPagedown:function(a){return 34===a.keyCode},isEscape:function(a){return 27===a.keyCode},_data:null,_index:null,_$header:null,_$footer:null,_bindEvents:function(){this.$el.on("mousedown."+this.id,".textcomplete-item",a.proxy(this._onClick,this)),this.$el.on("mouseover."+this.id,".textcomplete-item",a.proxy(this._onMouseover,this)),this.$inputEl.on("keydown."+this.id,a.proxy(this._onKeydown,this))},_onClick:function(b){var c=a(b.target);b.preventDefault(),b.originalEvent.keepTextCompleteDropdown=this.id,c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item"));var d=this.data[parseInt(c.data("index"),10)];this.completer.select(d.value,d.strategy);var e=this;setTimeout(function(){e.deactivate()},0)},_onMouseover:function(b){var c=a(b.target);b.preventDefault(),c.hasClass("textcomplete-item")||(c=c.closest(".textcomplete-item")),this._index=parseInt(c.data("index"),10),this._activateIndexedItem()},_onKeydown:function(a){this.shown&&(this.isUp(a)?(a.preventDefault(),this._up()):this.isDown(a)?(a.preventDefault(),this._down()):this.isEnter(a)?(a.preventDefault(),this._enter()):this.isPageup(a)?(a.preventDefault(),this._pageup()):this.isPagedown(a)?(a.preventDefault(),this._pagedown()):this.isEscape(a)&&(a.preventDefault(),this.deactivate()))},_up:function(){0===this._index?this._index=this.data.length-1:this._index-=1,this._activateIndexedItem(),this._setScroll()},_down:function(){this._index===this.data.length-1?this._index=0:this._index+=1,this._activateIndexedItem(),this._setScroll()},_enter:function(){var a=this.data[parseInt(this._getActiveElement().data("index"),10)];this.completer.select(a.value,a.strategy),this.deactivate()},_pageup:function(){var b=0,c=this._getActiveElement().position().top-this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top+a(this).outerHeight()>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_pagedown:function(){var b=this.data.length-1,c=this._getActiveElement().position().top+this.$el.innerHeight();this.$el.children().each(function(d){return a(this).position().top>c?(b=d,!1):void 0}),this._index=b,this._activateIndexedItem(),this._setScroll()},_activateIndexedItem:function(){this.$el.find(".textcomplete-item.active").removeClass("active"),this._getActiveElement().addClass("active")},_getActiveElement:function(){return this.$el.children(".textcomplete-item:nth("+this._index+")")},_setScroll:function(){var a=this._getActiveElement(),b=a.position().top,c=a.outerHeight(),d=this.$el.innerHeight(),e=this.$el.scrollTop();0===this._index||this._index==this.data.length-1||0>b?this.$el.scrollTop(b+e):b+c>d&&this.$el.scrollTop(b+c+e-d)},_buildContents:function(a){var b,d,e,f="";for(d=0;d',f+=b.strategy.template(b.value),f+="");return f},_renderHeader:function(b){if(this.header){this._$header||(this._$header=a('
  • ').prependTo(this.$el));var c=a.isFunction(this.header)?this.header(b):this.header;this._$header.html(c)}},_renderFooter:function(b){if(this.footer){this._$footer||(this._$footer=a('').appendTo(this.$el));var c=a.isFunction(this.footer)?this.footer(b):this.footer;this._$footer.html(c)}},_renderContents:function(a){this._$footer?this._$footer.before(a):this.$el.append(a)},_applyPlacement:function(a){return-1!==this.placement.indexOf("top")?a={top:"auto",bottom:this.$el.parent().height()-a.top+a.lineHeight,left:a.left}:(a.bottom="auto",delete a.lineHeight),-1!==this.placement.indexOf("absleft")?a.left=0:-1!==this.placement.indexOf("absright")&&(a.right=0,a.left="auto"),a}}),a.fn.textcomplete.Dropdown=b}(jQuery),+function(a){"use strict";function b(b){a.extend(this,b),this.cache&&(this.search=c(this.search))}var c=function(a){var b={};return function(c,d){b[c]?d(b[c]):a.call(this,c,function(a){b[c]=(b[c]||[]).concat(a),d.apply(null,arguments)})}};b.parse=function(c){return a.map(c,function(a){return new b(a)})},a.extend(b.prototype,{match:null,replace:null,search:null,cache:!1,context:function(){return!0},index:2,template:function(a){return a},idProperty:null}),a.fn.textcomplete.Strategy=b}(jQuery),+function(a){"use strict";function b(){}var c=Date.now||function(){return(new Date).getTime()},d=function(a,b){var d,e,f,g,h,i=function(){var j=c()-g;b>j?d=setTimeout(i,b-j):(d=null,h=a.apply(f,e),f=e=null)};return function(){return f=this,e=arguments,g=c(),d||(d=setTimeout(i,b)),h}};a.extend(b.prototype,{id:null,completer:null,el:null,$el:null,option:null,initialize:function(b,c,e){this.el=b,this.$el=a(b),this.id=c.id+this.constructor.name,this.completer=c,this.option=e,this.option.debounce&&(this._onKeyup=d(this._onKeyup,this.option.debounce)),this._bindEvents()},destroy:function(){this.$el.off("."+this.id),this.$el=this.el=this.completer=null},select:function(){throw new Error("Not implemented")},getCaretPosition:function(){var a=this._getCaretRelativePosition(),b=this.$el.offset();return a.top+=b.top,a.left+=b.left,a},focus:function(){this.$el.focus()},_bindEvents:function(){this.$el.on("keyup."+this.id,a.proxy(this._onKeyup,this))},_onKeyup:function(a){this._skipSearch(a)||this.completer.trigger(this.getTextFromHeadToCaret(),!0)},_skipSearch:function(a){switch(a.keyCode){case 13:case 40:case 38:return!0}if(a.ctrlKey)switch(a.keyCode){case 78:case 80:return!0}}}),a.fn.textcomplete.Adapter=b}(jQuery),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}b.DIV_PROPERTIES={left:-9999,position:"absolute",top:0,whiteSpace:"pre-wrap"},b.COPY_PROPERTIES=["border-width","font-family","font-size","font-style","font-variant","font-weight","height","letter-spacing","word-spacing","line-height","text-decoration","text-align","width","padding-top","padding-right","padding-bottom","padding-left","margin-top","margin-right","margin-bottom","margin-left","border-style","box-sizing","tab-size"],a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c){var d=this.getTextFromHeadToCaret(),e=this.el.value.substring(this.el.selectionEnd),f=c.replace(b);a.isArray(f)&&(e=f[1]+e,f=f[0]),d=d.replace(c.match,f),this.$el.val(d+e),this.el.selectionStart=this.el.selectionEnd=d.length},_getCaretRelativePosition:function(){var b=a("
    ").css(this._copyCss()).text(this.getTextFromHeadToCaret()),c=a("").text(".").appendTo(b);this.$el.before(b);var d=c.position();return d.top+=c.height()-this.$el.scrollTop(),d.lineHeight=c.height(),b.remove(),d},_copyCss:function(){return a.extend({overflow:this.el.scrollHeight>this.el.offsetHeight?"scroll":"auto"},b.DIV_PROPERTIES,this._getStyles())},_getStyles:function(a){var c=a("
    ").css(["color"]).color;return"undefined"!=typeof c?function(){return this.$el.css(b.COPY_PROPERTIES)}:function(){var c=this.$el,d={};return a.each(b.COPY_PROPERTIES,function(a,b){d[b]=c.css(b)}),d}}(a),getTextFromHeadToCaret:function(){return this.el.value.substring(0,this.el.selectionEnd)}}),a.fn.textcomplete.Textarea=b}(jQuery),+function(a){"use strict";function b(b,d,e){this.initialize(b,d,e),a(""+c+"").css({position:"absolute",top:-9999,left:-9999}).insertBefore(b)}var c="吶";a.extend(b.prototype,a.fn.textcomplete.Textarea.prototype,{select:function(b,c){var d=this.getTextFromHeadToCaret(),e=this.el.value.substring(d.length),f=c.replace(b);a.isArray(f)&&(e=f[1]+e,f=f[0]),d=d.replace(c.match,f),this.$el.val(d+e),this.el.focus();var g=this.el.createTextRange();g.collapse(!0),g.moveEnd("character",d.length),g.moveStart("character",d.length),g.select()},getTextFromHeadToCaret:function(){this.el.focus();var a=document.selection.createRange();a.moveStart("character",-this.el.value.length);var b=a.text.split(c);return 1===b.length?b[0]:b[1]}}),a.fn.textcomplete.IETextarea=b}(jQuery),+function(a){"use strict";function b(a,b,c){this.initialize(a,b,c)}a.extend(b.prototype,a.fn.textcomplete.Adapter.prototype,{select:function(b,c){var d=this.getTextFromHeadToCaret(),e=window.getSelection(),f=e.getRangeAt(0),g=f.cloneRange();g.selectNodeContents(f.startContainer);var h=g.toString(),i=h.substring(f.startOffset),j=c.replace(b);a.isArray(j)&&(i=j[1]+i,j=j[0]),d=d.replace(c.match,j),f.selectNodeContents(f.startContainer),f.deleteContents();var k=document.createTextNode(d+i);f.insertNode(k),f.setStart(k,d.length),f.collapse(!0),e.removeAllRanges(),e.addRange(f)},_getCaretRelativePosition:function(){var b=window.getSelection().getRangeAt(0).cloneRange(),c=document.createElement("span");b.insertNode(c),b.selectNodeContents(c),b.deleteContents();var d=a(c),e=d.offset();e.left-=this.$el.offset().left,e.top+=d.height()-this.$el.offset().top,e.lineHeight=d.height(),d.remove();var f=this.$el.attr("dir")||this.$el.css("direction");return"rtl"===f&&(e.left-=this.listView.$el.width()),e},getTextFromHeadToCaret:function(){var a=window.getSelection().getRangeAt(0),b=a.cloneRange();return b.selectNodeContents(a.startContainer),b.toString().substring(0,a.startOffset)}}),a.fn.textcomplete.ContentEditable=b}(jQuery); diff --git a/src/categories.js b/src/categories.js index 87c540aaf1..3061ef8425 100644 --- a/src/categories.js +++ b/src/categories.js @@ -62,7 +62,6 @@ var async = require('async'), category.nextStart = results.topics.nextStart; category.pageCount = results.pageCount; category.isIgnored = results.isIgnored[0]; - category.topic_row_size = 'col-md-9'; plugins.fireHook('filter:category.get', {category: category, uid: data.uid}, function(err, data) { callback(err, data ? data.category : null); diff --git a/src/categories/create.js b/src/categories/create.js index ce293d1c42..0db3cad0c3 100644 --- a/src/categories/create.js +++ b/src/categories/create.js @@ -32,7 +32,7 @@ module.exports = function(Categories) { order: order, link: '', numRecentReplies: 1, - class: 'col-md-3 col-xs-6', + class: 'col-md-3 col-xs-12', imageClass: 'auto' }; diff --git a/src/categories/topics.js b/src/categories/topics.js index 50dba67bbd..805ab30c4a 100644 --- a/src/categories/topics.js +++ b/src/categories/topics.js @@ -74,37 +74,29 @@ module.exports = function(Categories) { }); }; - Categories.onNewPostMade = function(postData, callback) { - topics.getTopicFields(postData.tid, ['cid', 'pinned'], function(err, topicData) { - if (err) { - return callback(err); - } + Categories.onNewPostMade = function(cid, pinned, postData, callback) { + if (!cid || !postData) { + return callback(); + } - if (!topicData || !topicData.cid) { - return callback(); - } - - var cid = topicData.cid; - - async.parallel([ - function(next) { - db.sortedSetAdd('cid:' + cid + ':pids', postData.timestamp, postData.pid, next); - }, - function(next) { - db.incrObjectField('category:' + cid, 'post_count', next); - }, - function(next) { - if (parseInt(topicData.pinned, 10) === 1) { - next(); - } else { - db.sortedSetAdd('cid:' + cid + ':tids', postData.timestamp, postData.tid, next); - } - }, - function(next) { - db.sortedSetIncrBy('cid:' + cid + ':tids:posts', 1, postData.tid, next); + async.parallel([ + function(next) { + db.sortedSetAdd('cid:' + cid + ':pids', postData.timestamp, postData.pid, next); + }, + function(next) { + db.incrObjectField('category:' + cid, 'post_count', next); + }, + function(next) { + if (parseInt(pinned, 10) === 1) { + next(); + } else { + db.sortedSetAdd('cid:' + cid + ':tids', postData.timestamp, postData.tid, next); } - ], callback); - }); + }, + function(next) { + db.sortedSetIncrBy('cid:' + cid + ':tids:posts', 1, postData.tid, next); + } + ], callback); }; }; diff --git a/src/controllers/accounts.js b/src/controllers/accounts.js index 8b966fbd26..51cc3daf82 100644 --- a/src/controllers/accounts.js +++ b/src/controllers/accounts.js @@ -356,7 +356,7 @@ accountsController.accountSettings = function(req, res, next) { }, userGroups: function(next) { groups.getUserGroups([userData.uid], next); - }, + }, languages: function(next) { languages.list(next); } @@ -434,7 +434,7 @@ accountsController.uploadPicture = function (req, res, next) { user.setUserFields(updateUid, {uploadedpicture: image.url, picture: image.url}); - res.json([{name: userPhoto.name, url: nconf.get('relative_path') + image.url}]); + res.json([{name: userPhoto.name, url: image.url.startsWith('http') ? image.url : nconf.get('relative_path') + image.url}]); } if (err) { diff --git a/src/controllers/categories.js b/src/controllers/categories.js index 18bbd57f8b..8896894680 100644 --- a/src/controllers/categories.js +++ b/src/controllers/categories.js @@ -4,6 +4,8 @@ var categoriesController = {}, async = require('async'), nconf = require('nconf'), validator = require('validator'), + + db = require('../database'), privileges = require('../privileges'), user = require('../user'), categories = require('../categories'), @@ -249,6 +251,11 @@ categoriesController.get = function(req, res, next) { categories.getCategoryById(payload, next); }, function(categoryData, next) { + if (categoryData.link) { + db.incrObjectField('category:' + categoryData.cid, 'timesClicked'); + return res.redirect(categoryData.link); + } + var breadcrumbs = [ { text: categoryData.name, @@ -264,16 +271,13 @@ categoriesController.get = function(req, res, next) { }); }, function(categoryData, next) { - if (categoryData.link) { - return res.redirect(categoryData.link); - } - categories.getRecentTopicReplies(categoryData.children, uid, function(err) { next(err, categoryData); }); }, function (categoryData, next) { categoryData.privileges = userPrivileges; + categoryData.showSelect = categoryData.privileges.editable; res.locals.metaTags = [ { diff --git a/src/controllers/index.js b/src/controllers/index.js index 6aba26bb1f..74cce358e5 100644 --- a/src/controllers/index.js +++ b/src/controllers/index.js @@ -26,7 +26,7 @@ var Controllers = { accounts: require('./accounts'), static: require('./static'), api: require('./api'), - admin: require('./admin'), + admin: require('./admin') }; @@ -52,15 +52,18 @@ Controllers.home = function(req, res, next) { Controllers.reset = function(req, res, next) { if (req.params.code) { user.reset.validate(req.params.code, function(err, valid) { + if (err) { + return next(err); + } res.render('reset_code', { valid: valid, - reset_code: req.params.code ? req.params.code : null, + code: req.params.code ? req.params.code : null, breadcrumbs: helpers.buildBreadcrumbs([{text: '[[reset_password:reset_password]]', url: '/reset'}, {text: '[[reset_password:update_password]]'}]) }); }); } else { res.render('reset', { - reset_code: req.params.code ? req.params.code : null, + code: req.params.code ? req.params.code : null, breadcrumbs: helpers.buildBreadcrumbs([{text: '[[reset_password:reset_password]]'}]) }); } diff --git a/src/controllers/search.js b/src/controllers/search.js index 7ed16d8bb8..775d430783 100644 --- a/src/controllers/search.js +++ b/src/controllers/search.js @@ -23,26 +23,6 @@ searchController.search = function(req, res, next) { return next(err); } - if (!req.params.term) { - var results = { - time: 0, - search_query: '', - posts: [], - users: [], - tags: [], - categories: categories, - breadcrumbs: breadcrumbs, - expandSearch: true - }; - plugins.fireHook('filter:search.build', {data: {}, results: results}, function(err, data) { - if (err) { - return next(err); - } - res.render('search', data.results); - }); - return; - } - req.params.term = validator.escape(req.params.term); var page = Math.max(1, parseInt(req.query.page, 10)) || 1; if (req.query.categories && !Array.isArray(req.query.categories)) { @@ -77,7 +57,7 @@ searchController.search = function(req, res, next) { results.breadcrumbs = breadcrumbs; results.categories = categories; results.expandSearch = false; - + plugins.fireHook('filter:search.build', {data: data, results: results}, function(err, data) { if (err) { return next(err); diff --git a/src/database/mongo.js b/src/database/mongo.js index e7ee038f96..522f9fffdc 100644 --- a/src/database/mongo.js +++ b/src/database/mongo.js @@ -112,8 +112,14 @@ createIndex('objects', {_key: 1, value: -1}, {background:true}); createIndex('objects', {expireAt: 1}, {expireAfterSeconds:0, background:true}); - createIndex('search', {content:'text'}, {background:true}); - createIndex('search', {key: 1, id: 1}, {background:true}); + + createIndex('searchtopic', {content: 'text', uid: 1, cid: 1}, {background:true}); + createIndex('searchtopic', {id: 1}, {background:true}); + + + createIndex('searchpost', {content: 'text', uid: 1, cid: 1}, {background:true}); + createIndex('searchpost', {id: 1}, {background:true}); + if (typeof callback === 'function') { callback(); diff --git a/src/database/mongo/main.js b/src/database/mongo/main.js index c2cf4883bf..eba0b3b14b 100644 --- a/src/database/mongo/main.js +++ b/src/database/mongo/main.js @@ -5,15 +5,18 @@ var winston = require('winston'); module.exports = function(db, module) { var helpers = module.helpers.mongo; - module.searchIndex = function(key, content, id, callback) { + module.searchIndex = function(key, data, id, callback) { callback = callback || function() {}; - var data = { - id: id, - key: key, - content: content + var setData = { + id: id }; + for(var field in data) { + if (data.hasOwnProperty(field) && data[field]) { + setData[field] = data[field].toString(); + } + } - db.collection('search').update({key:key, id:id}, {$set:data}, {upsert:true, w: 1}, function(err) { + db.collection('search' + key).update({id: id}, {$set: setData}, {upsert:true, w: 1}, function(err) { if(err) { winston.error('Error indexing ' + err.message); } @@ -21,13 +24,35 @@ module.exports = function(db, module) { }); }; - module.search = function(key, term, limit, callback) { - db.collection('search').find({ $text: { $search: term }, key: key}, {limit: limit}).toArray(function(err, results) { - if(err) { + module.search = function(key, data, limit, callback) { + var searchQuery = {}; + + if (data.content) { + searchQuery.$text = {$search: data.content}; + } + + if (Array.isArray(data.cid) && data.cid.length) { + if (data.cid.length > 1) { + searchQuery.cid = {$in: data.cid.map(String)}; + } else { + searchQuery.cid = data.cid[0].toString(); + } + } + + if (Array.isArray(data.uid) && data.uid.length) { + if (data.uid.length > 1) { + searchQuery.uid = {$in: data.uid.map(String)}; + } else { + searchQuery.uid = data.uid[0].toString(); + } + } + + db.collection('search' + key).find(searchQuery, {limit: limit}).toArray(function(err, results) { + if (err) { return callback(err); } - if(!results || !results.length) { + if (!results || !results.length) { return callback(null, []); } @@ -41,7 +66,12 @@ module.exports = function(db, module) { module.searchRemove = function(key, id, callback) { callback = callback || helpers.noop; - db.collection('search').remove({key:key, id:id}, callback); + if (!id) { + return callback(); + } + db.collection('search' + key).remove({id: id}, function(err, res) { + callback(err); + }); }; module.flushdb = function(callback) { diff --git a/src/database/redis.js b/src/database/redis.js index 727705c130..716a8fd715 100644 --- a/src/database/redis.js +++ b/src/database/redis.js @@ -9,7 +9,7 @@ utils = require('./../../public/src/utils.js'), redis, connectRedis, - reds, + redisSearch, redisClient, postSearch, topicSearch; @@ -41,7 +41,7 @@ try { redis = require('redis'); connectRedis = require('connect-redis')(session); - reds = require('reds'); + redisSearch = require('redisearch'); } catch (err) { winston.error('Unable to initialize Redis! Is Redis installed? Error :' + err.message); process.exit(); @@ -56,12 +56,8 @@ ttl: 60 * 60 * 24 * 14 }); - reds.createClient = function () { - return reds.client || (reds.client = redisClient); - }; - - module.postSearch = reds.createSearch('nodebbpostsearch'); - module.topicSearch = reds.createSearch('nodebbtopicsearch'); + module.postSearch = redisSearch.createSearch('nodebbpostsearch', redisClient); + module.topicSearch = redisSearch.createSearch('nodebbtopicsearch', redisClient); require('./redis/main')(redisClient, module); require('./redis/hash')(redisClient, module); diff --git a/src/database/redis/main.js b/src/database/redis/main.js index ebad6ae8e6..b09574e10f 100644 --- a/src/database/redis/main.js +++ b/src/database/redis/main.js @@ -1,36 +1,30 @@ "use strict"; module.exports = function(redisClient, module) { - module.searchIndex = function(key, content, id, callback) { - if (key === 'post') { - module.postSearch.index(content, id, callback); - } else if(key === 'topic') { - module.topicSearch.index(content, id, callback); - } + module.searchIndex = function(key, data, id, callback) { + var method = key === 'post' ? module.postSearch : module.topicSearch; + + method.index(data, id, function(err, res) { + callback(err); + }); }; - module.search = function(key, term, limit, callback) { - function search(searchObj, callback) { - searchObj - .query(term) - .between(0, limit - 1) - .type('or') - .end(callback); - } + module.search = function(key, data, limit, callback) { + var method = key === 'post' ? module.postSearch : module.topicSearch; - if(key === 'post') { - search(module.postSearch, callback); - } else if(key === 'topic') { - search(module.topicSearch, callback); - } + method.query(data, 0, limit - 1, callback); }; module.searchRemove = function(key, id, callback) { - if(key === 'post') { - module.postSearch.remove(id, callback); - } else if(key === 'topic') { - module.topicSearch.remove(id, callback); + callback = callback || function() {}; + if (!id) { + return callback(); } + var method = key === 'post' ? module.postSearch : module.topicSearch; + + method.remove(id, function(err, res) { + callback(err); + }); }; module.flushdb = function(callback) { diff --git a/src/emailer.js b/src/emailer.js index b358e170fb..aeca1ecab8 100644 --- a/src/emailer.js +++ b/src/emailer.js @@ -62,7 +62,8 @@ var fs = require('fs'), plaintext: translated[1], template: template, uid: uid, - pid: params.pid + pid: params.pid, + fromUid: params.fromUid }); callback(); } else { diff --git a/src/file.js b/src/file.js index 543c5011fa..6be1cd37c8 100644 --- a/src/file.js +++ b/src/file.js @@ -8,11 +8,20 @@ var fs = require('fs'), Magic = mmmagic.Magic, mime = require('mime'), - meta= require('./meta'); + meta = require('./meta'), + utils = require('../public/src/utils'); var file = {}; file.saveFileToLocal = function(filename, folder, tempPath, callback) { + /* + * remarkable doesn't allow spaces in hyperlinks, once that's fixed, remove this. + */ + filename = filename.split('.'); + filename.forEach(function(name, idx) { + filename[idx] = utils.slugify(name); + }); + filename = filename.join('.'); var uploadPath = path.join(nconf.get('base_dir'), nconf.get('upload_path'), folder, filename); diff --git a/src/groups.js b/src/groups.js index 25c8ff1bdc..43a376106f 100644 --- a/src/groups.js +++ b/src/groups.js @@ -226,9 +226,9 @@ var async = require('async'), return callback(err); } results.base.name = !options.unescape ? validator.escape(results.base.name) : results.base.name; - results.base.description = options.unescape ? validator.escape(results.base.description) : results.base.description; + results.base.description = !options.unescape ? validator.escape(results.base.description) : results.base.description; results.base.descriptionParsed = descriptionParsed; - results.base.userTitle = options.unescape ? validator.escape(results.base.userTitle) : results.base.userTitle; + results.base.userTitle = !options.unescape ? validator.escape(results.base.userTitle) : results.base.userTitle; results.base.userTitleEnabled = results.base.userTitleEnabled ? !!parseInt(results.base.userTitleEnabled, 10) : true; results.base.createtimeISO = utils.toISOString(results.base.createtime); results.base.members = results.users.filter(Boolean); @@ -440,7 +440,6 @@ var async = require('async'), return ephemeralGroups.indexOf(slug) !== -1; })); }, - async.apply(db.isObjectFields, 'groupslug:groupname', slugs), async.apply(db.isSortedSetMembers, 'groups:createtime', name) ], function(err, results) { if (err) { @@ -448,7 +447,7 @@ var async = require('async'), } callback(null, results.map(function(result) { - return result[0] || result[1] || result[2]; + return result[0] || result[1]; })); }); } else { @@ -457,16 +456,19 @@ var async = require('async'), function(next) { next(null, ephemeralGroups.indexOf(slug) !== -1); }, - async.apply(db.isObjectField, 'groupslug:groupname', slug), async.apply(db.isSortedSetMember, 'groups:createtime', name) ], function(err, results) { - callback(err, !err ? (results[0] || results[1] || results[2]) : null); + callback(err, !err ? (results[0] || results[1]) : null); }); } }; Groups.existsBySlug = function(slug, callback) { - db.isObjectField('groupslug:groupname', slug, callback); + if (Array.isArray(slug)) { + db.isObjectFields('groupslug:groupName', slug, callback); + } else { + db.isObjectField('groupslug:groupname', slug, callback); + } }; Groups.create = function(data, callback) { @@ -499,7 +501,7 @@ var async = require('async'), deleted: '0', hidden: data.hidden || '0', system: system ? '1' : '0', - 'private': data.private || '1' + private: data.private || '1' }, tasks = [ async.apply(db.sortedSetAdd, 'groups:createtime', now, data.name), @@ -538,14 +540,26 @@ var async = require('async'), } var payload = { - userTitle: values.userTitle || '', - userTitleEnabled: values.userTitleEnabled === true ? '1' : '0', - description: values.description || '', - icon: values.icon || '', - labelColor: values.labelColor || '#000000', - hidden: values.hidden === true ? '1' : '0', - 'private': values.private === false ? '0' : '1' - }; + description: values.description || '', + icon: values.icon || '', + labelColor: values.labelColor || '#000000' + }; + + if (values.hasOwnProperty('userTitle')) { + payload.userTitle = values.userTitle || ''; + } + + if (values.hasOwnProperty('userTitleEnabled')) { + payload.userTitleEnabled = values.userTitleEnabled ? '1' : '0'; + } + + if (values.hasOwnProperty('hidden')) { + payload.hidden = values.hidden ? '1' : '0'; + } + + if (values.hasOwnProperty('private')) { + payload.private = values.private ? '1' : '0'; + } async.series([ async.apply(updatePrivacy, groupName, values.private), @@ -566,9 +580,15 @@ var async = require('async'), }; function updatePrivacy(groupName, newValue, callback) { - // Grab the group's current privacy value + if (!newValue) { + return callback(); + } + Groups.getGroupFields(groupName, ['private'], function(err, currentValue) { - currentValue = currentValue.private === '1'; // Now a Boolean + if (err) { + return callback(err); + } + currentValue = currentValue.private === '1'; if (currentValue !== newValue && currentValue === true) { // Group is now public, so all pending users are automatically considered members diff --git a/src/meta.js b/src/meta.js index b58ac1a9b2..4aa65f6719 100644 --- a/src/meta.js +++ b/src/meta.js @@ -29,7 +29,7 @@ var async = require('async'), Meta.userOrGroupExists = function(slug, callback) { async.parallel([ async.apply(user.exists, slug), - async.apply(groups.exists, slug) + async.apply(groups.existsBySlug, slug) ], function(err, results) { callback(err, results ? results.some(function(result) { return result; }) : false); }); diff --git a/src/meta/css.js b/src/meta/css.js index e0f0412e1c..d45b61f280 100644 --- a/src/meta/css.js +++ b/src/meta/css.js @@ -49,6 +49,7 @@ module.exports = function(Meta) { source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/css/smoothness/jquery-ui-1.10.4.custom.min.css";'; source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.css";'; + source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/jquery/textcomplete/jquery.textcomplete.css";'; source += '\n@import (inline) "..' + path.sep + '..' + path.sep + 'public/vendor/colorpicker/colorpicker.css";'; acpSource = '\n@import "..' + path.sep + 'public/less/admin/admin";\n' + source; diff --git a/src/meta/js.js b/src/meta/js.js index 177d8b03c8..3d8ced839c 100644 --- a/src/meta/js.js +++ b/src/meta/js.js @@ -31,6 +31,7 @@ module.exports = function(Meta) { 'public/vendor/visibility/visibility.min.js', 'public/vendor/bootstrap/js/bootstrap.min.js', 'public/vendor/jquery/bootstrap-tagsinput/bootstrap-tagsinput.min.js', + 'public/vendor/jquery/textcomplete/jquery.textcomplete.min.js', 'public/vendor/requirejs/require.js', 'public/vendor/bootbox/bootbox.min.js', 'public/vendor/tinycon/tinycon.js', @@ -43,10 +44,11 @@ module.exports = function(Meta) { 'public/src/utils.js', 'public/src/app.js', 'public/src/ajaxify.js', - 'public/src/variables.js', - 'public/src/widgets.js', + 'public/src/components.js', + 'public/src/overrides.js', 'public/src/translator.js', - 'public/src/overrides.js' + 'public/src/variables.js', + 'public/src/widgets.js' ], rjs: [] } diff --git a/src/postTools.js b/src/postTools.js index 9ddc7b999e..647d796841 100644 --- a/src/postTools.js +++ b/src/postTools.js @@ -19,7 +19,7 @@ var winston = require('winston'), var cache = LRU({ max: 1048576, - length: function (n) { return n.length }, + length: function (n) { return n.length; }, maxAge: 1000 * 60 * 60 }); @@ -63,22 +63,32 @@ var cache = LRU({ }, topic: function(next) { var tid = postData.tid; - posts.isMain(data.pid, function(err, isMainPost) { + async.parallel({ + cid: function(next) { + topics.getTopicField(tid, 'cid', next); + }, + isMain: function(next) { + posts.isMain(data.pid, next); + } + }, function(err, results) { if (err) { return next(err); } options.tags = options.tags || []; - if (!isMainPost) { + if (!results.isMain) { return next(null, { tid: tid, + cid: results.cid, isMainPost: false }); } var topicData = { tid: tid, + cid: results.cid, + uid: postData.uid, mainPid: data.pid, title: title, slug: tid + '/' + utils.slugify(title) @@ -98,8 +108,10 @@ var cache = LRU({ topics.getTopicTagsObjects(tid, function(err, tags) { next(err, { tid: tid, + cid: results.cid, + uid: postData.uid, title: validator.escape(title), - isMainPost: isMainPost, + isMainPost: results.isMain, tags: tags }); }); @@ -114,6 +126,7 @@ var cache = LRU({ if (err) { return callback(err); } + postData.cid = results.topic.cid; results.content = results.postData.content; plugins.fireHook('action:post.edit', postData); @@ -156,7 +169,7 @@ var cache = LRU({ } if (isDelete) { - cache.del(postData.pid); + cache.del(pid); posts.delete(pid, callback); } else { posts.restore(pid, function(err, postData) { diff --git a/src/posts/create.js b/src/posts/create.js index a235b2f3f0..1bce6e69c7 100644 --- a/src/posts/create.js +++ b/src/posts/create.js @@ -68,7 +68,13 @@ module.exports = function(Posts) { topics.onNewPostMade(postData, next); }, function(next) { - categories.onNewPostMade(postData, next); + topics.getTopicFields(tid, ['cid', 'pinned'], function(err, topicData) { + if (err) { + return next(err); + } + postData.cid = topicData.cid; + categories.onNewPostMade(topicData.cid, topicData.pinned, postData, next); + }); }, function(next) { db.sortedSetAdd('posts:pid', timestamp, postData.pid, next); diff --git a/src/posts/delete.js b/src/posts/delete.js index bd130ade46..f47ff28e88 100644 --- a/src/posts/delete.js +++ b/src/posts/delete.js @@ -9,16 +9,19 @@ var async = require('async'), module.exports = function(Posts) { Posts.delete = function(pid, callback) { - Posts.setPostField(pid, 'deleted', 1, function(err) { - if (err) { - return callback(err); - } - - Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'timestamp'], function(err, postData) { - if (err) { - return callback(err); - } - + var postData; + async.waterfall([ + function(next) { + Posts.setPostField(pid, 'deleted', 1, next); + }, + function(next) { + Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'timestamp'], next); + }, + function(_post, next) { + postData = _post; + topics.getTopicField(_post.tid, 'cid', next); + }, + function(cid, next) { plugins.fireHook('action:post.delete', pid); async.parallel([ @@ -26,7 +29,7 @@ module.exports = function(Posts) { updateTopicTimestamp(postData.tid, next); }, function(next) { - removeFromCategoryRecentPosts(pid, postData.tid, next); + db.sortedSetRemove('cid:' + cid + ':pids', pid, next); }, function(next) { Posts.dismissFlag(pid, next); @@ -34,21 +37,25 @@ module.exports = function(Posts) { ], function(err) { callback(err, postData); }); - }); - }); + } + ], callback); }; Posts.restore = function(pid, callback) { - Posts.setPostField(pid, 'deleted', 0, function(err) { - if (err) { - return callback(err); - } - - Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'timestamp'], function(err, postData) { - if (err) { - return callback(err); - } - + var postData; + async.waterfall([ + function(next) { + Posts.setPostField(pid, 'deleted', 0, next); + }, + function(next) { + Posts.getPostFields(pid, ['pid', 'tid', 'uid', 'content', 'timestamp'], next); + }, + function(_post, next) { + postData = _post; + topics.getTopicField(_post.tid, 'cid', next); + }, + function(cid, next) { + postData.cid = cid; plugins.fireHook('action:post.restore', postData); async.parallel([ @@ -56,13 +63,13 @@ module.exports = function(Posts) { updateTopicTimestamp(postData.tid, next); }, function(next) { - addToCategoryRecentPosts(pid, postData.tid, postData.timestamp, next); + db.sortedSetAdd('cid:' + cid + ':pids', postData.timestamp, pid, next); } ], function(err) { callback(err, postData); }); - }); - }); + } + ], callback); }; function updateTopicTimestamp(tid, callback) { @@ -84,26 +91,6 @@ module.exports = function(Posts) { }); } - function removeFromCategoryRecentPosts(pid, tid, callback) { - topics.getTopicField(tid, 'cid', function(err, cid) { - if (err) { - return callback(err); - } - - db.sortedSetRemove('cid:' + cid + ':pids', pid, callback); - }); - } - - function addToCategoryRecentPosts(pid, tid, timestamp, callback) { - topics.getTopicField(tid, 'cid', function(err, cid) { - if (err) { - return callback(err); - } - - db.sortedSetAdd('cid:' + cid + ':pids', timestamp, pid, callback); - }); - } - Posts.purge = function(pid, callback) { Posts.exists(pid, function(err, exists) { if (err || !exists) { diff --git a/src/posts/recent.js b/src/posts/recent.js index 4a970a6435..d41ef5342e 100644 --- a/src/posts/recent.js +++ b/src/posts/recent.js @@ -13,16 +13,16 @@ module.exports = function(Posts) { }; Posts.getRecentPosts = function(uid, start, stop, term, callback) { - var since = terms.day; + var min = 0; if (terms[term]) { - since = terms[term]; + min = Date.now() - terms[term]; } var count = parseInt(stop, 10) === -1 ? stop : stop - start + 1; async.waterfall([ function(next) { - db.getSortedSetRevRangeByScore('posts:pid', start, count, '+inf', Date.now() - since, next); + db.getSortedSetRevRangeByScore('posts:pid', start, count, '+inf', min, next); }, function(pids, next) { privileges.posts.filter('read', pids, uid, next); diff --git a/src/routes/feeds.js b/src/routes/feeds.js index 0f2f6082b6..58d44cbce5 100644 --- a/src/routes/feeds.js +++ b/src/routes/feeds.js @@ -84,8 +84,8 @@ function generateForTopic(req, res, next) { feed.item({ title: 'Reply to ' + topicData.title + ' on ' + dateStamp, description: postData.content, - url: nconf.get('url') + '/topic/' + topicData.slug + '#' + postData.pid, - author: postData.username, + url: nconf.get('url') + '/topic/' + topicData.slug + (postData.index ? '/' + (postData.index + 1) : ''), + author: postData.user ? postData.user.username : '', date: dateStamp }); } @@ -144,8 +144,8 @@ function generateForCategory(req, res, next) { if (err) { return next(err); } - sendFeed(feed, res); - }); + sendFeed(feed, res); + }); }); } @@ -183,8 +183,8 @@ function generateForPopular(req, res, next) { return next(err); } sendFeed(feed, res); - }); - }); + }); + }); } function disabledRSS(req, res, next) { @@ -201,13 +201,13 @@ function generateForTopics(options, set, req, res, next) { if (err) { return next(err); } - + generateTopicsFeed(options, data.topics, function(err, feed) { if (err) { return next(err); } - sendFeed(feed, res); - }); + sendFeed(feed, res); + }); }); } @@ -215,7 +215,7 @@ function generateTopicsFeed(feedOptions, feedTopics, callback) { var tids = feedTopics.map(function(topic) { return topic ? topic.tid : null; }); - + topics.getMainPids(tids, function(err, pids) { if (err) { return callback(err); @@ -252,7 +252,7 @@ function generateTopicsFeed(feedOptions, feedTopics, callback) { }); callback(null, feed); }); - }); + }); } function generateForRecentPosts(req, res, next) { diff --git a/src/routes/index.js b/src/routes/index.js index 37ce391e54..5efca3f067 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -173,50 +173,51 @@ module.exports = function(app, middleware) { function handle404(app, middleware) { app.use(function(req, res, next) { - if (!plugins.hasListeners('action:meta.override404')) { - var relativePath = nconf.get('relative_path'); - var isLanguage = new RegExp('^' + relativePath + '/language/[\\w]{2,}/.*.json'), - isClientScript = new RegExp('^' + relativePath + '\\/src\\/.+\\.js'); - - if (isClientScript.test(req.url)) { - res.type('text/javascript').status(200).send(''); - } else if (isLanguage.test(req.url)) { - res.status(200).json({}); - } else if (req.accepts('html')) { - if (process.env.NODE_ENV === 'development') { - winston.warn('Route requested but not found: ' + req.url); - } - - res.status(404); - - if (res.locals.isAPI) { - return res.json({path: req.path, error: 'not-found'}); - } - - middleware.buildHeader(req, res, function() { - res.render('404', {path: req.path}); - }); - } else { - res.status(404).type('txt').send('Not found'); - } - } else { - plugins.fireHook('action:meta.override404', { + if (plugins.hasListeners('action:meta.override404')) { + return plugins.fireHook('action:meta.override404', { req: req, res: res, error: {} }); } + + var relativePath = nconf.get('relative_path'); + var isLanguage = new RegExp('^' + relativePath + '/language/[\\w]{2,}/.*.json'), + isClientScript = new RegExp('^' + relativePath + '\\/src\\/.+\\.js'); + + if (isClientScript.test(req.url)) { + res.type('text/javascript').status(200).send(''); + } else if (isLanguage.test(req.url)) { + res.status(200).json({}); + } else if (req.accepts('html')) { + if (process.env.NODE_ENV === 'development') { + winston.warn('Route requested but not found: ' + req.url); + } + + res.status(404); + + if (res.locals.isAPI) { + return res.json({path: req.path, error: 'not-found'}); + } + + middleware.buildHeader(req, res, function() { + res.render('404', {path: req.path}); + }); + } else { + res.status(404).type('txt').send('Not found'); + } }); } function handleErrors(app, middleware) { app.use(function(err, req, res, next) { - winston.error(req.path + '\n', err.stack); - if (err.code === 'EBADCSRFTOKEN') { + winston.error(req.path + '\n', err.message) return res.sendStatus(403); } + winston.error(req.path + '\n', err.stack); + if (parseInt(err.status, 10) === 302 && err.path) { return res.locals.isAPI ? res.status(302).json(err.path) : res.redirect(err.path); } diff --git a/src/search.js b/src/search.js index 8ccdf77d78..063ebfa142 100644 --- a/src/search.js +++ b/src/search.js @@ -43,7 +43,7 @@ search.search = function(data, callback) { }; if (searchIn === 'posts' || searchIn === 'titles' || searchIn === 'titlesposts') { - searchInContent(query, data, done); + searchInContent(data, done); } else if (searchIn === 'users') { searchInUsers(query, data.uid, done); } else if (searchIn === 'tags') { @@ -53,91 +53,87 @@ search.search = function(data, callback) { } }; -function searchInContent(query, data, callback) { +function searchInContent(data, callback) { data.uid = data.uid || 0; async.parallel({ - pids: function(next) { - if (data.searchIn === 'posts' || data.searchIn === 'titlesposts') { - search.searchQuery('post', query, next); - } else { - next(null, []); - } + searchCids: function(next) { + getSearchCids(data, next); }, - tids: function(next) { - if (data.searchIn === 'titles' || data.searchIn === 'titlesposts') { - search.searchQuery('topic', query, next); - } else { - next(null, []); - } - }, - searchCategories: function(next) { - getSearchCategories(data, next); - } - }, function (err, results) { - if (err) { - return callback(err); - } - - var matchCount = 0; - if (!results || (!results.pids.length && !results.tids.length)) { - return callback(null, {matches: [], matchCount: matchCount}); - } - - async.waterfall([ - function(next) { - getMainPids(results.tids, next); - }, - function(mainPids, next) { - results.pids.forEach(function(pid) { - if (mainPids.indexOf(pid.toString()) === -1) { - mainPids.push(pid); - } - }); - - privileges.posts.filter('read', mainPids, data.uid, next); - }, - function(pids, next) { - filterAndSort(pids, data, results.searchCategories, next); - }, - function(pids, next) { - matchCount = pids.length; - if (data.page) { - var start = Math.max(0, (data.page - 1)) * 10; - pids = pids.slice(start, start + 10); - } - - posts.getPostSummaryByPids(pids, data.uid, {stripTags: true, parse: false}, next); - }, - function(posts, next) { - next(null, {matches: posts, matchCount: matchCount}); - } - ], callback); - }); -} - -function filterAndSort(pids, data, searchCategories, callback) { - async.parallel({ - posts: function(next) { - getMatchedPosts(pids, data, searchCategories, next); - }, - postedByUid: function(next) { - if (data.postedBy) { - user.getUidByUsername(data.postedBy, next); - } else { - next(); - } + searchUids: function(next) { + getSearchUids(data, next); } }, function(err, results) { if (err) { return callback(err); } - if (!results.posts) { + + async.parallel({ + pids: function(next) { + if (data.searchIn === 'posts' || data.searchIn === 'titlesposts') { + search.searchQuery('post', data.query, results.searchCids, results.searchUids, next); + } else { + next(null, []); + } + }, + tids: function(next) { + if (data.searchIn === 'titles' || data.searchIn === 'titlesposts') { + search.searchQuery('topic', data.query, results.searchCids, results.searchUids, next); + } else { + next(null, []); + } + } + }, function (err, results) { + if (err) { + return callback(err); + } + + var matchCount = 0; + if (!results || (!results.pids.length && !results.tids.length)) { + return callback(null, {matches: [], matchCount: matchCount}); + } + + async.waterfall([ + function(next) { + topics.getMainPids(results.tids, next); + }, + function(mainPids, next) { + results.pids = mainPids.concat(results.pids).filter(function(pid, index, array) { + return pid && array.indexOf(pid) === index; + }); + + privileges.posts.filter('read', results.pids, data.uid, next); + }, + function(pids, next) { + filterAndSort(pids, data, next); + }, + function(pids, next) { + matchCount = pids.length; + if (data.page) { + var start = Math.max(0, (data.page - 1)) * 10; + pids = pids.slice(start, start + 10); + } + + posts.getPostSummaryByPids(pids, data.uid, {stripTags: true, parse: false}, next); + }, + function(posts, next) { + next(null, {matches: posts, matchCount: matchCount}); + } + ], callback); + }); + }); +} + +function filterAndSort(pids, data, callback) { + getMatchedPosts(pids, data, function(err, posts) { + if (err) { + return callback(err); + } + + if (!Array.isArray(posts) || !posts.length) { return callback(null, pids); } - var posts = results.posts.filter(Boolean); + posts = posts.filter(Boolean); - posts = filterByUser(posts, results.postedByUid); - posts = filterByCategories(posts, searchCategories); posts = filterByPostcount(posts, data.replies, data.repliesFilter); posts = filterByTimerange(posts, data.timeRange, data.timeFilter); @@ -151,25 +147,19 @@ function filterAndSort(pids, data, searchCategories, callback) { }); } -function getMatchedPosts(pids, data, searchCategories, callback) { +function getMatchedPosts(pids, data, callback) { var postFields = ['pid', 'tid', 'timestamp']; var topicFields = []; var categoryFields = []; - if (data.postedBy) { - postFields.push('uid'); - } - - if (searchCategories.length || (data.sortBy && data.sortBy.startsWith('category.'))) { - topicFields.push('cid'); - } - if (data.replies) { topicFields.push('postcount'); } if (data.sortBy) { - if (data.sortBy.startsWith('topic.')) { + if (data.sortBy.startsWith('category')) { + topicFields.push('cid'); + } else if (data.sortBy.startsWith('topic.')) { topicFields.push(data.sortBy.split('.')[1]); } else if (data.sortBy.startsWith('user.')) { postFields.push('uid'); @@ -282,25 +272,6 @@ function getMatchedPosts(pids, data, searchCategories, callback) { ], callback); } -function filterByUser(posts, postedByUid) { - if (postedByUid) { - postedByUid = parseInt(postedByUid, 10); - posts = posts.filter(function(post) { - return parseInt(post.uid, 10) === postedByUid; - }); - } - return posts; -} - -function filterByCategories(posts, searchCategories) { - if (searchCategories.length) { - posts = posts.filter(function(post) { - return post.topic && searchCategories.indexOf(post.topic.cid) !== -1; - }); - } - return posts; -} - function filterByPostcount(posts, postCount, repliesFilter) { postCount = parseInt(postCount, 10); if (postCount) { @@ -391,7 +362,7 @@ function sortPosts(posts, data) { } } -function getSearchCategories(data, callback) { +function getSearchCids(data, callback) { if (!Array.isArray(data.categories) || !data.categories.length || data.categories.indexOf('all') !== -1) { return callback(null, []); } @@ -441,6 +412,14 @@ function getChildrenCids(cids, uid, callback) { }); } +function getSearchUids(data, callback) { + if (data.postedBy) { + user.getUidsByUsernames(Array.isArray(data.postedBy) ? data.postedBy : [data.postedBy], callback); + } else { + callback(null, []); + } +} + function searchInUsers(query, uid, callback) { user.search({query: query, uid: uid}, function(err, results) { if (err) { @@ -460,26 +439,12 @@ function searchInTags(query, callback) { }); } -function getMainPids(tids, callback) { - if (!Array.isArray(tids) || !tids.length) { - return callback(null, []); - } - - topics.getTopicsFields(tids, ['mainPid'], function(err, topics) { - if (err) { - return callback(err); - } - topics = topics.map(function(topic) { - return topic && topic.mainPid && topic.mainPid.toString(); - }).filter(Boolean); - callback(null, topics); - }); -} - -search.searchQuery = function(index, query, callback) { +search.searchQuery = function(index, content, cids, uids, callback) { plugins.fireHook('filter:search.query', { index: index, - query: query + content: content, + cid: cids, + uid: uids }, callback); }; diff --git a/src/socket.io/admin.js b/src/socket.io/admin.js index 6860fb6e66..6b61d523f3 100644 --- a/src/socket.io/admin.js +++ b/src/socket.io/admin.js @@ -304,7 +304,7 @@ SocketAdmin.getMoreEvents = function(socket, next, callback) { if (start < 0) { return callback(null, {data: [], next: next}); } - var end = next + 10; + var end = start + 10; events.getEvents(start, end, function(err, events) { if (err) { return callback(err); diff --git a/src/socket.io/categories.js b/src/socket.io/categories.js index 9f20697ef9..d98cb0c21f 100644 --- a/src/socket.io/categories.js +++ b/src/socket.io/categories.js @@ -118,4 +118,8 @@ SocketCategories.ignore = function(socket, cid, callback) { }); }; +SocketCategories.isModerator = function(socket, cid, callback) { + user.isModerator(socket.uid, cid, callback); +}; + module.exports = SocketCategories; diff --git a/src/socket.io/meta.js b/src/socket.io/meta.js index c801381a1f..21fdeca35e 100644 --- a/src/socket.io/meta.js +++ b/src/socket.io/meta.js @@ -70,6 +70,10 @@ SocketMeta.rooms.enter = function(socket, data, callback) { socket.currentRoom = data.enter; if (data.enter.indexOf('topic') !== -1) { data.uid = socket.uid; + data.picture = validator.escape(data.picture); + data.username = validator.escape(data.username); + data.userslug = validator.escape(data.userslug); + websockets.in(data.enter).emit('event:user_enter', data); } } diff --git a/src/socket.io/posts.js b/src/socket.io/posts.js index e8f5ef9a63..b49e796e7a 100644 --- a/src/socket.io/posts.js +++ b/src/socket.io/posts.js @@ -50,6 +50,10 @@ SocketPosts.reply = function(socket, data, callback) { socket.emit('event:new_post', result); SocketPosts.notifyOnlineUsers(socket.uid, result); + + if (data.lock) { + socketTopics.doTopicAction('lock', 'event:topic_locked', socket, {tids: [postData.topic.tid], cid: postData.topic.cid}); + } }); }; @@ -362,9 +366,9 @@ SocketPosts.purge = function(socket, data, callback) { }); callback(); - }); + }); } - + if (!data || !parseInt(data.pid, 10)) { return callback(new Error('[[error:invalid-data]]')); } @@ -500,7 +504,7 @@ SocketPosts.flag = function(socket, pid, callback) { } notifications.push(notification, results.admins.concat(results.moderators), next); }); - } + } ], callback); }; diff --git a/src/socket.io/topics.js b/src/socket.io/topics.js index cd42d1fde4..c270082953 100644 --- a/src/socket.io/topics.js +++ b/src/socket.io/topics.js @@ -17,7 +17,7 @@ var nconf = require('nconf'), meta = require('../meta'), events = require('../events'), utils = require('../../public/src/utils'), - SocketPosts = require('./posts'), + SocketTopics = {}; @@ -41,6 +41,10 @@ SocketTopics.post = function(socket, data, callback) { return callback(err); } + if (data.lock) { + SocketTopics.doTopicAction('lock', 'event:topic_locked', socket, {tids: [result.topicData.tid], cid: result.topicData.cid}); + } + callback(null, result.topicData); socket.emit('event:new_post', {posts: [result.postData]}); socket.emit('event:new_topic', result.topicData); @@ -233,6 +237,7 @@ SocketTopics.unpin = function(socket, data, callback) { }; SocketTopics.doTopicAction = function(action, event, socket, data, callback) { + callback = callback || function() {}; if (!socket.uid) { return; } @@ -312,7 +317,7 @@ SocketTopics.movePost = function(socket, data, callback) { return callback(err); } - SocketPosts.sendNotificationToPostOwner(data.pid, socket.uid, 'notifications:moved_your_post'); + require('./posts').sendNotificationToPostOwner(data.pid, socket.uid, 'notifications:moved_your_post'); callback(); }); }); @@ -550,4 +555,13 @@ SocketTopics.loadMoreTags = function(socket, data, callback) { }); }; +SocketTopics.isModerator = function(socket, tid, callback) { + topics.getTopicField(tid, 'cid', function(err, cid) { + if (err) { + return callback(err); + } + user.isModerator(socket.uid, cid, callback); + }); +}; + module.exports = SocketTopics; diff --git a/src/socket.io/user.js b/src/socket.io/user.js index 7170cf27d0..807666ff18 100644 --- a/src/socket.io/user.js +++ b/src/socket.io/user.js @@ -281,7 +281,7 @@ SocketUser.follow = function(socket, data, callback) { if (!socket.uid || !data) { return; } - + var userData; async.waterfall([ function(next) { toggleFollow('follow', socket.uid, data.uid, next); @@ -289,7 +289,8 @@ SocketUser.follow = function(socket, data, callback) { function(next) { user.getUserFields(socket.uid, ['username', 'userslug'], next); }, - function(userData, next) { + function(_userData, next) { + userData = _userData; notifications.create({ bodyShort: '[[notifications:user_started_following_you, ' + userData.username + ']]', nid: 'follow:' + data.uid + ':uid:' + socket.uid, @@ -297,6 +298,7 @@ SocketUser.follow = function(socket, data, callback) { }, next); }, function(notification, next) { + notification.user = userData; notifications.push(notification, [data.uid], next); } ], callback); diff --git a/src/threadTools.js b/src/threadTools.js index 96d8198291..76ea84c9df 100644 --- a/src/threadTools.js +++ b/src/threadTools.js @@ -21,7 +21,7 @@ var async = require('async'), }; function toggleDelete(tid, uid, isDelete, callback) { - topics.getTopicFields(tid, ['tid', 'cid', 'deleted', 'title', 'mainPid'], function(err, topicData) { + topics.getTopicFields(tid, ['tid', 'cid', 'uid', 'deleted', 'title', 'mainPid'], function(err, topicData) { if (err) { return callback(err); } @@ -183,13 +183,16 @@ var async = require('async'), categories.moveRecentReplies(tid, oldCid, cid); - topics.setTopicField(tid, 'cid', cid, callback); - - plugins.fireHook('action:topic.move', { - tid: tid, - fromCid: oldCid, - toCid: cid, - uid: uid + topics.setTopicField(tid, 'cid', cid, function(err) { + if (err) { + return callback(err); + } + plugins.fireHook('action:topic.move', { + tid: tid, + fromCid: oldCid, + toCid: cid, + uid: uid + }); }); }); }; diff --git a/src/topics.js b/src/topics.js index aecdcb1000..0cc153e85e 100644 --- a/src/topics.js +++ b/src/topics.js @@ -287,13 +287,17 @@ var async = require('async'), }; Topics.getMainPids = function(tids, callback) { + if (!Array.isArray(tids) || !tids.length) { + return callback(null, []); + } + Topics.getTopicsFields(tids, ['mainPid'], function(err, topicData) { if (err) { return callback(err); } var mainPids = topicData.map(function(topic) { - return topic ? topic.mainPid : null; + return topic && topic.mainPid; }); callback(null, mainPids); }); diff --git a/src/topics/create.js b/src/topics/create.js index 36a73948d7..5988f637e2 100644 --- a/src/topics/create.js +++ b/src/topics/create.js @@ -93,7 +93,6 @@ module.exports = function(Topics) { Topics.post = function(data, callback) { var uid = data.uid, - handle = data.handle, title = data.title, content = data.content, cid = data.cid; @@ -135,7 +134,7 @@ module.exports = function(Topics) { Topics.create({uid: uid, title: title, cid: cid, thumb: data.thumb, tags: data.tags}, next); }, function(tid, next) { - Topics.reply({uid:uid, tid:tid, handle: handle, content:content, req: data.req}, next); + Topics.reply({uid:uid, tid:tid, handle: data.handle, content:content, req: data.req}, next); }, function(postData, next) { async.parallel({ @@ -185,30 +184,23 @@ module.exports = function(Topics) { Topics.reply = function(data, callback) { var tid = data.tid, uid = data.uid, - toPid = data.toPid, - handle = data.handle, content = data.content, postData; async.waterfall([ function(next) { async.parallel({ - exists: function(next) { - Topics.exists(tid, next); - }, - locked: function(next) { - Topics.isLocked(tid, next); - }, - canReply: function(next) { - privileges.topics.can('topics:reply', tid, uid, next); - } + exists: async.apply(Topics.exists, tid), + locked: async.apply(Topics.isLocked, tid), + canReply: async.apply(privileges.topics.can, 'topics:reply', tid, uid), + isAdmin: async.apply(user.isAdministrator, uid) }, next); }, function(results, next) { if (!results.exists) { return next(new Error('[[error:no-topic]]')); } - if (results.locked) { + if (results.locked && !results.isAdmin) { return next(new Error('[[error:topic-locked]]')); } if (!results.canReply) { @@ -229,7 +221,7 @@ module.exports = function(Topics) { checkContentLength(content, next); }, function(next) { - posts.create({uid: uid, tid: tid, handle: handle, content: content, toPid: toPid, ip: data.req ? data.req.ip : null}, next); + posts.create({uid: uid, tid: tid, handle: data.handle, content: content, toPid: data.toPid, ip: data.req ? data.req.ip : null}, next); }, function(data, next) { postData = data; diff --git a/src/topics/teaser.js b/src/topics/teaser.js index 87e2baa86f..adb4f77b5c 100644 --- a/src/topics/teaser.js +++ b/src/topics/teaser.js @@ -7,6 +7,7 @@ var async = require('async'), db = require('../database'), user = require('../user'), posts = require('../posts'), + plugins = require('../plugins'), utils = require('../../public/src/utils'); @@ -27,7 +28,7 @@ module.exports = function(Topics) { } }); - posts.getPostsFields(teaserPids, ['pid', 'uid', 'timestamp', 'tid'], function(err, postData) { + posts.getPostsFields(teaserPids, ['pid', 'uid', 'timestamp', 'tid', 'content'], function(err, postData) { if (err) { return callback(err); } @@ -61,7 +62,9 @@ module.exports = function(Topics) { return tidToPost[topic.tid]; }); - callback(null, teasers); + plugins.fireHook('filter:teasers.get', {teasers: teasers}, function(err, data) { + callback(err, data ? data.teasers : null); + }); }); }); }; diff --git a/src/user.js b/src/user.js index 109e67a3f0..2b9c411ab8 100644 --- a/src/user.js +++ b/src/user.js @@ -120,7 +120,7 @@ var async = require('async'), if (user.picture) { if (user.picture === user.uploadedpicture) { - user.picture = user.uploadedpicture = user.picture.indexOf('http') === -1 ? nconf.get('relative_path') + user.picture : user.picture; + user.picture = user.uploadedpicture = user.picture.startsWith('http') ? user.picture : nconf.get('relative_path') + user.picture; } else { user.picture = User.createGravatarURLFromEmail(user.email); } @@ -158,7 +158,7 @@ var async = require('async'), if (now - parseInt(userOnlineTime, 10) < 300000) { return callback(); } - db.sortedSetAdd('users:online', now, uid, next); + db.sortedSetAdd('users:online', now, uid, next); }, function(next) { topics.pushUnreadCount(uid); @@ -334,6 +334,18 @@ var async = require('async'), db.getObjectField('username:uid', username, callback); }; + User.getUidsByUsernames = function(usernames, callback) { + db.getObjectFields('username:uid', usernames, function(err, users) { + if (err) { + return callback(err); + } + var uids = usernames.map(function(username) { + return users[username]; + }); + callback(null, uids); + }); + }; + User.getUidByUserslug = function(userslug, callback) { if (!userslug) { return callback(); diff --git a/src/user/digest.js b/src/user/digest.js index 06ea1aa967..02799c0bf3 100644 --- a/src/user/digest.js +++ b/src/user/digest.js @@ -46,7 +46,6 @@ var async = require('async'), return topicObj; }); - return; data.interval = interval; diff --git a/src/user/notifications.js b/src/user/notifications.js index c3ea4b9c23..fe9ffc80b8 100644 --- a/src/user/notifications.js +++ b/src/user/notifications.js @@ -109,34 +109,38 @@ var async = require('async'), return callback(err); } - var pids = notifications.map(function(notification) { - return notification ? notification.pid : null; - }); + UserNotifications.generateNotificationPaths(notifications, uid, callback); + }); + }; - generatePostPaths(pids, uid, function(err, pidToPaths) { - if (err) { - return callback(err); + UserNotifications.generateNotificationPaths = function (notifications, uid, callback) { + var pids = notifications.map(function(notification) { + return notification ? notification.pid : null; + }); + + generatePostPaths(pids, uid, function(err, pidToPaths) { + if (err) { + return callback(err); + } + + notifications = notifications.map(function(notification, index) { + if (!notification) { + return null; } - notifications = notifications.map(function(notification, index) { - if (!notification) { - return null; - } + notification.path = pidToPaths[notification.pid] || notification.path || ''; - notification.path = pidToPaths[notification.pid] || notification.path || ''; + if (notification.nid.startsWith('chat')) { + notification.path = nconf.get('relative_path') + '/chats/' + notification.user.userslug; + } else if (notification.nid.startsWith('follow')) { + notification.path = nconf.get('relative_path') + '/user/' + notification.user.userslug; + } - if (notification.nid.startsWith('chat')) { - notification.path = nconf.get('relative_path') + '/chats/' + notification.user.userslug; - } else if (notification.nid.startsWith('follow')) { - notification.path = nconf.get('relative_path') + '/user/' + notification.user.userslug; - } - - notification.datetimeISO = utils.toISOString(notification.datetime); - return notification; - }); - - callback(null, notifications); + notification.datetimeISO = utils.toISOString(notification.datetime); + return notification; }); + + callback(null, notifications); }); }; diff --git a/src/views/admin/manage/flags.tpl b/src/views/admin/manage/flags.tpl index a87f450a47..f463d829d2 100644 --- a/src/views/admin/manage/flags.tpl +++ b/src/views/admin/manage/flags.tpl @@ -50,7 +50,7 @@

    - + Posted in {posts.category.name}, Read More diff --git a/src/views/partials/data/category.tpl b/src/views/partials/data/category.tpl new file mode 100644 index 0000000000..2144c8a84c --- /dev/null +++ b/src/views/partials/data/category.tpl @@ -0,0 +1 @@ +data-tid="{topics.tid}" data-index="{topics.index}" data-cid="{topics.cid}" itemprop="itemListElement" \ No newline at end of file diff --git a/src/views/partials/data/topic.tpl b/src/views/partials/data/topic.tpl new file mode 100644 index 0000000000..125926e94f --- /dev/null +++ b/src/views/partials/data/topic.tpl @@ -0,0 +1 @@ +data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.user.username}" data-userslug="{posts.user.userslug}" data-index="{posts.index}" data-timestamp="{posts.timestamp}" data-votes="{posts.votes}" itemscope itemtype="http://schema.org/Comment" \ No newline at end of file diff --git a/src/views/partials/requirejs-config.tpl b/src/views/partials/requirejs-config.tpl new file mode 100644 index 0000000000..932c571f4a --- /dev/null +++ b/src/views/partials/requirejs-config.tpl @@ -0,0 +1,12 @@ + \ No newline at end of file diff --git a/src/views/partials/variables/account.tpl b/src/views/partials/variables/account.tpl new file mode 100644 index 0000000000..cc63770339 --- /dev/null +++ b/src/views/partials/variables/account.tpl @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/views/partials/variables/account/edit.tpl b/src/views/partials/variables/account/edit.tpl new file mode 100644 index 0000000000..d8e5d259cc --- /dev/null +++ b/src/views/partials/variables/account/edit.tpl @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/views/partials/variables/account/profile.tpl b/src/views/partials/variables/account/profile.tpl new file mode 100644 index 0000000000..be91685112 --- /dev/null +++ b/src/views/partials/variables/account/profile.tpl @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/views/partials/variables/category.tpl b/src/views/partials/variables/category.tpl new file mode 100644 index 0000000000..984002411a --- /dev/null +++ b/src/views/partials/variables/category.tpl @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/src/views/partials/variables/groups/details.tpl b/src/views/partials/variables/groups/details.tpl new file mode 100644 index 0000000000..0cefdde305 --- /dev/null +++ b/src/views/partials/variables/groups/details.tpl @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/views/partials/variables/reset_code.tpl b/src/views/partials/variables/reset_code.tpl new file mode 100644 index 0000000000..ab630d7cff --- /dev/null +++ b/src/views/partials/variables/reset_code.tpl @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/views/partials/variables/tag.tpl b/src/views/partials/variables/tag.tpl new file mode 100644 index 0000000000..486ba8245c --- /dev/null +++ b/src/views/partials/variables/tag.tpl @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/views/partials/variables/topic.tpl b/src/views/partials/variables/topic.tpl new file mode 100644 index 0000000000..4f3c15180d --- /dev/null +++ b/src/views/partials/variables/topic.tpl @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/webserver.js b/src/webserver.js index 849be56879..bd4acb231f 100644 --- a/src/webserver.js +++ b/src/webserver.js @@ -110,7 +110,7 @@ if(nconf.get('ssl')) { emitter.emit('nodebb:ready'); }); - server.setTimeout(10000); + server.setTimout && server.setTimeout(10000); module.exports.listen = function(callback) { logger.init(app); diff --git a/tests/groups.js b/tests/groups.js index 0025000828..a4d5c9da30 100644 --- a/tests/groups.js +++ b/tests/groups.js @@ -186,13 +186,22 @@ describe('Groups', function() { }); describe('.update()', function() { + before(function(done) { + Groups.create({ + name: 'updateTestGroup', + description: 'bar', + system: 0, + hidden: 0 + }, done); + }); + it('should change an aspect of a group', function(done) { - Groups.update('foo', { + Groups.update('updateTestGroup', { description: 'baz' }, function(err) { if (err) return done(err); - Groups.get('foo', {}, function(err, groupObj) { + Groups.get('updateTestGroup', {}, function(err, groupObj) { if (err) return done(err); assert.strictEqual('baz', groupObj.description); @@ -203,16 +212,16 @@ describe('Groups', function() { }); it('should rename a group if the name was updated', function(done) { - Groups.update('foo', { - name: 'foobar?' + Groups.update('updateTestGroup', { + name: 'updateTestGroup?' }, function(err) { if (err) return done(err); - Groups.get('foobar?', {}, function(err, groupObj) { + Groups.get('updateTestGroup?', {}, function(err, groupObj) { if (err) return done(err); - assert.strictEqual('foobar?', groupObj.name); - assert.strictEqual('foobar', groupObj.slug); + assert.strictEqual('updateTestGroup?', groupObj.name); + assert.strictEqual('updatetestgroup', groupObj.slug); done(); });