Merge remote-tracking branch 'origin/master' into webserver.js-refactor

v1.18.x
psychobunny 11 years ago
commit ee5596fad5

@ -44,12 +44,12 @@
"alert.banned.message": "Estás baneado, serás desconectado!", "alert.banned.message": "Estás baneado, serás desconectado!",
"alert.unfollow": "Ya no estás siguiendo a %1!", "alert.unfollow": "Ya no estás siguiendo a %1!",
"alert.follow": "Estás siguiendo a %1!", "alert.follow": "Estás siguiendo a %1!",
"posts": "Publicaciones", "posts": "Posts",
"views": "Visitas", "views": "Visitas",
"posted": "publicado", "posted": "publicado",
"in": "en", "in": "en",
"recentposts": "Publicaciones Recientes", "recentposts": "Publicaciones Recientes",
"recentips": "Recently Logged In IPs", "recentips": "Conexions recientes de estas IP's",
"online": "Conectado", "online": "Conectado",
"away": "No disponible", "away": "No disponible",
"dnd": "No molestar", "dnd": "No molestar",

@ -8,7 +8,7 @@
"user.edit": "Editando \"%1\"", "user.edit": "Editando \"%1\"",
"user.following": "Gente que sigue %1 ", "user.following": "Gente que sigue %1 ",
"user.followers": "Seguidores de %1", "user.followers": "Seguidores de %1",
"user.posts": "Posts made by %1", "user.posts": "Posteos de %1",
"user.favourites": "Publicaciones favoritas de %1 ", "user.favourites": "Publicaciones favoritas de %1 ",
"user.settings": "Preferencias del Usuario" "user.settings": "Preferencias del Usuario"
} }

@ -11,7 +11,7 @@
"reply": "Responder", "reply": "Responder",
"edit": "Editar", "edit": "Editar",
"delete": "Borrar", "delete": "Borrar",
"restore": "Restore", "restore": "Restaurar",
"move": "Mover", "move": "Mover",
"fork": "Bifurcar", "fork": "Bifurcar",
"banned": "baneado", "banned": "baneado",
@ -19,15 +19,15 @@
"share": "Compartir", "share": "Compartir",
"tools": "Herramientas", "tools": "Herramientas",
"flag": "Reportar", "flag": "Reportar",
"bookmark_instructions": "Click here to return to your last position or close to discard.", "bookmark_instructions": "Click aqui para restablecer la ultima posicion del post o cierralo para descartar cambios.",
"flag_title": "Reportar esta publicación a los moderadores", "flag_title": "Reportar esta publicación a los moderadores",
"deleted_message": "Este tema ha sido borrado. Solo los miembros con privilegios pueden verlo.", "deleted_message": "Este tema ha sido borrado. Solo los miembros con privilegios pueden verlo.",
"following_topic.title": "Siguendo tema", "following_topic.title": "Siguendo tema",
"following_topic.message": "Ahora recibiras notificaciones cuando alguien publique en este tema.", "following_topic.message": "Ahora recibiras notificaciones cuando alguien publique en este tema.",
"not_following_topic.title": "No sigues este tema", "not_following_topic.title": "No sigues este tema",
"not_following_topic.message": "No recibiras notificaciones de este tema.", "not_following_topic.message": "No recibiras notificaciones de este tema.",
"login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "login_to_subscribe": "Por favor, conectate para subscribirte a este tema.",
"markAsUnreadForAll.success": "Topic marked as unread for all.", "markAsUnreadForAll.success": "Marcar todo como leeido.",
"watch": "Seguir", "watch": "Seguir",
"share_this_post": "Compartir este post", "share_this_post": "Compartir este post",
"thread_tools.title": "Herramientas del Tema", "thread_tools.title": "Herramientas del Tema",
@ -66,17 +66,17 @@
"composer.title_placeholder": "Ingresa el titulo de tu tema", "composer.title_placeholder": "Ingresa el titulo de tu tema",
"composer.write": "Escribe", "composer.write": "Escribe",
"composer.preview": "Previsualización", "composer.preview": "Previsualización",
"composer.help": "Help", "composer.help": "Ayuda",
"composer.discard": "Descartar", "composer.discard": "Descartar",
"composer.submit": "Enviar", "composer.submit": "Enviar",
"composer.replying_to": "Respondiendo a", "composer.replying_to": "Respondiendo a",
"composer.new_topic": "Nuevo Tema", "composer.new_topic": "Nuevo Tema",
"composer.uploading": "uploading...", "composer.uploading": "cargando...",
"composer.thumb_url_label": "Paste a topic thumbnail URL", "composer.thumb_url_label": "Agregar imagen destacada a este tema.",
"composer.thumb_title": "Add a thumbnail to this topic", "composer.thumb_title": "Agregar miniatura a este tema.",
"composer.thumb_url_placeholder": "http://example.com/thumb.png", "composer.thumb_url_placeholder": "http://ejemplo.com/mini.png",
"composer.thumb_file_label": "Or upload a file", "composer.thumb_file_label": "Cargar una foto",
"composer.thumb_remove": "Clear fields", "composer.thumb_remove": "Limpiar campos.",
"composer.drag_and_drop_images": "Arrastra las imagenes aqui", "composer.drag_and_drop_images": "Arrastra las imagenes aqui",
"composer.upload_instructions": "Carga tus imagenes con solo arrastrarlas aqui." "composer.upload_instructions": "Carga tus imagenes con solo arrastrarlas aqui."
} }

@ -8,7 +8,7 @@
"user.edit": "Edite \"%1\"", "user.edit": "Edite \"%1\"",
"user.following": "Personnes que %1 suit", "user.following": "Personnes que %1 suit",
"user.followers": "Personnes qui suivent %1", "user.followers": "Personnes qui suivent %1",
"user.posts": "Posts made by %1", "user.posts": "Message écrit par %1",
"user.favourites": "Messages favoris de %1", "user.favourites": "Messages favoris de %1",
"user.settings": "Préférences Utilisateur" "user.settings": "Préférences Utilisateur"
} }

@ -11,7 +11,7 @@
"reply": "Répondre", "reply": "Répondre",
"edit": "Editer", "edit": "Editer",
"delete": "Supprimer", "delete": "Supprimer",
"restore": "Restore", "restore": "Restaurer",
"move": "Déplacer", "move": "Déplacer",
"fork": "Scinder", "fork": "Scinder",
"banned": "bannis", "banned": "bannis",
@ -19,15 +19,15 @@
"share": "Partager", "share": "Partager",
"tools": "Outils", "tools": "Outils",
"flag": "Signaler", "flag": "Signaler",
"bookmark_instructions": "Click here to return to your last position or close to discard.", "bookmark_instructions": "Cliquer ici pour retourner à votre dernière position ou fermer pour ignorer.",
"flag_title": "Signaler ce post pour modération", "flag_title": "Signaler ce post pour modération",
"deleted_message": "Ce sujet a été supprimé. Seuls les utilsateurs avec les droits d'administration peuvent le voir.", "deleted_message": "Ce sujet a été supprimé. Seuls les utilsateurs avec les droits d'administration peuvent le voir.",
"following_topic.title": "Sujet suivi", "following_topic.title": "Sujet suivi",
"following_topic.message": "Vous recevrez désormais des notifications lorsque quelqu'un postera dans ce sujet.", "following_topic.message": "Vous recevrez désormais des notifications lorsque quelqu'un postera dans ce sujet.",
"not_following_topic.title": "Sujet non suivi", "not_following_topic.title": "Sujet non suivi",
"not_following_topic.message": "Vous ne recevrez plus de notifications pour ce sujet.", "not_following_topic.message": "Vous ne recevrez plus de notifications pour ce sujet.",
"login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "login_to_subscribe": "Veuillez vous enregistrer ou vous connecter afin de souscrire à ce sujet.",
"markAsUnreadForAll.success": "Topic marked as unread for all.", "markAsUnreadForAll.success": "Sujet marqué comme non lu pour tout le monde.",
"watch": "Suivre", "watch": "Suivre",
"share_this_post": "Partager ce message", "share_this_post": "Partager ce message",
"thread_tools.title": "Outils du Fil", "thread_tools.title": "Outils du Fil",
@ -66,17 +66,17 @@
"composer.title_placeholder": "Entrer le titre du sujet ici...", "composer.title_placeholder": "Entrer le titre du sujet ici...",
"composer.write": "Ecriture", "composer.write": "Ecriture",
"composer.preview": "Aperçu", "composer.preview": "Aperçu",
"composer.help": "Help", "composer.help": "Aide",
"composer.discard": "Abandon", "composer.discard": "Abandon",
"composer.submit": "Envoi", "composer.submit": "Envoi",
"composer.replying_to": "Répondre à", "composer.replying_to": "Répondre à",
"composer.new_topic": "Nouveau Sujet", "composer.new_topic": "Nouveau Sujet",
"composer.uploading": "uploading...", "composer.uploading": "téléchargement...",
"composer.thumb_url_label": "Paste a topic thumbnail URL", "composer.thumb_url_label": "Coller une URL de vignette du sujet",
"composer.thumb_title": "Add a thumbnail to this topic", "composer.thumb_title": "Ajouter une vignette à ce sujet",
"composer.thumb_url_placeholder": "http://example.com/thumb.png", "composer.thumb_url_placeholder": "http://exemple.com/vignette.png",
"composer.thumb_file_label": "Or upload a file", "composer.thumb_file_label": "Ou télécharger un fichier",
"composer.thumb_remove": "Clear fields", "composer.thumb_remove": "Effacer les champs",
"composer.drag_and_drop_images": "Glisser-déposer ici les images", "composer.drag_and_drop_images": "Glisser-déposer ici les images",
"composer.upload_instructions": "Uploader des images par glisser-déposer." "composer.upload_instructions": "Uploader des images par glisser-déposer."
} }

@ -49,7 +49,7 @@
"posted": "skapt", "posted": "skapt",
"in": "i", "in": "i",
"recentposts": "Seneste innlegg", "recentposts": "Seneste innlegg",
"recentips": "Recently Logged In IPs", "recentips": "Seneste innloggede IP-er",
"online": "Online", "online": "Online",
"away": "Borte", "away": "Borte",
"dnd": "Ikke forsturr", "dnd": "Ikke forsturr",

@ -8,7 +8,7 @@
"user.edit": "Endrer \"%1\"", "user.edit": "Endrer \"%1\"",
"user.following": "Personer %1 følger", "user.following": "Personer %1 følger",
"user.followers": "Personer som følger %1", "user.followers": "Personer som følger %1",
"user.posts": "Posts made by %1", "user.posts": "Innlegg laget av %1",
"user.favourites": "%1 sine favoritt-innlegg", "user.favourites": "%1 sine favoritt-innlegg",
"user.settings": "Brukerinnstillinger" "user.settings": "Brukerinnstillinger"
} }

@ -11,7 +11,7 @@
"reply": "Svar", "reply": "Svar",
"edit": "Endre", "edit": "Endre",
"delete": "Slett", "delete": "Slett",
"restore": "Restore", "restore": "Gjenopprett",
"move": "Flytt", "move": "Flytt",
"fork": "Del", "fork": "Del",
"banned": "utestengt", "banned": "utestengt",
@ -19,15 +19,15 @@
"share": "Del", "share": "Del",
"tools": "Verktøy", "tools": "Verktøy",
"flag": "Rapporter", "flag": "Rapporter",
"bookmark_instructions": "Click here to return to your last position or close to discard.", "bookmark_instructions": "Klikk her for å returnere til din siste posisjon eller lukk for å forkaste.",
"flag_title": "Rapporter dette innlegget for granskning", "flag_title": "Rapporter dette innlegget for granskning",
"deleted_message": "Denne tråden har blitt slettet. Bare brukere med trådhåndterings-privilegier kan se den.", "deleted_message": "Denne tråden har blitt slettet. Bare brukere med trådhåndterings-privilegier kan se den.",
"following_topic.title": "Følger tråd", "following_topic.title": "Følger tråd",
"following_topic.message": "Du vil nå motta varsler når noen skriver i denne tråden.", "following_topic.message": "Du vil nå motta varsler når noen skriver i denne tråden.",
"not_following_topic.title": "Følger ikke tråd", "not_following_topic.title": "Følger ikke tråd",
"not_following_topic.message": "Du vil ikke lenger motta varsler fra denne tråden.", "not_following_topic.message": "Du vil ikke lenger motta varsler fra denne tråden.",
"login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "login_to_subscribe": "Vennligst registrer deg eller logg inn for å abonnere på denne tråden.",
"markAsUnreadForAll.success": "Topic marked as unread for all.", "markAsUnreadForAll.success": "Tråd markert som ulest for alle.",
"watch": "Overvåk", "watch": "Overvåk",
"share_this_post": "Del ditt innlegg", "share_this_post": "Del ditt innlegg",
"thread_tools.title": "Trådverktøy", "thread_tools.title": "Trådverktøy",
@ -66,17 +66,17 @@
"composer.title_placeholder": "Skriv din tråd-tittel her", "composer.title_placeholder": "Skriv din tråd-tittel her",
"composer.write": "Skriv", "composer.write": "Skriv",
"composer.preview": "Forhåndsvis", "composer.preview": "Forhåndsvis",
"composer.help": "Help", "composer.help": "Hjelp",
"composer.discard": "Forkast", "composer.discard": "Forkast",
"composer.submit": "Send", "composer.submit": "Send",
"composer.replying_to": "Svarer til", "composer.replying_to": "Svarer til",
"composer.new_topic": "Ny tråd", "composer.new_topic": "Ny tråd",
"composer.uploading": "uploading...", "composer.uploading": "laster opp...",
"composer.thumb_url_label": "Paste a topic thumbnail URL", "composer.thumb_url_label": "Lim inn som tråd-minatyr URL",
"composer.thumb_title": "Add a thumbnail to this topic", "composer.thumb_title": "Legg til minatyr til denne tråden",
"composer.thumb_url_placeholder": "http://example.com/thumb.png", "composer.thumb_url_placeholder": "http://example.com/thumb.png",
"composer.thumb_file_label": "Or upload a file", "composer.thumb_file_label": "Eller last opp en fil",
"composer.thumb_remove": "Clear fields", "composer.thumb_remove": "Tøm felter",
"composer.drag_and_drop_images": "Dra og slipp bilder her", "composer.drag_and_drop_images": "Dra og slipp bilder her",
"composer.upload_instructions": "Last opp bilder ved å dra og slippe dem." "composer.upload_instructions": "Last opp bilder ved å dra og slippe dem."
} }

@ -2,7 +2,7 @@
"new_topic_button": "Nytt ämne", "new_topic_button": "Nytt ämne",
"no_topics": "<strong>Det finns inga ämnen i denna kategori.</strong><br />Varför inte skapa ett?", "no_topics": "<strong>Det finns inga ämnen i denna kategori.</strong><br />Varför inte skapa ett?",
"posts": "inlägg", "posts": "inlägg",
"views": "tittningar", "views": "visningar",
"posted": "skapad", "posted": "skapad",
"browsing": "läser", "browsing": "läser",
"no_replies": "Ingen har svarat", "no_replies": "Ingen har svarat",

@ -10,16 +10,16 @@
"500.message": "Hoppsan! Verkar som att något gått snett!", "500.message": "Hoppsan! Verkar som att något gått snett!",
"register": "Registrera", "register": "Registrera",
"login": "Logga in", "login": "Logga in",
"please_log_in": "Please Log In", "please_log_in": "Var god logga in",
"posting_restriction_info": "Posting is currently restricted to registered members only, click here to log in.", "posting_restriction_info": "Man måste vara inloggad för att kunna skapa inlägg, klicka här för att logga in.",
"welcome_back": "Welcome Back ", "welcome_back": "Välkommen tillbaka",
"you_have_successfully_logged_in": "You have successfully logged in", "you_have_successfully_logged_in": "Inloggningen lyckades",
"logout": "Logga ut", "logout": "Logga ut",
"logout.title": "Du är nu utloggad.", "logout.title": "Du är nu utloggad.",
"logout.message": "Du är nu utloggad från NodeBB.", "logout.message": "Du är nu utloggad från NodeBB.",
"save_changes": "Spara ändringar", "save_changes": "Spara ändringar",
"close": "Stäng", "close": "Stäng",
"pagination": "Pagination", "pagination": "Siduppdelning",
"header.admin": "Admin", "header.admin": "Admin",
"header.recent": "Senaste", "header.recent": "Senaste",
"header.unread": "Olästa", "header.unread": "Olästa",
@ -49,7 +49,7 @@
"posted": "svarade", "posted": "svarade",
"in": "i", "in": "i",
"recentposts": "Senaste ämnena", "recentposts": "Senaste ämnena",
"recentips": "Recently Logged In IPs", "recentips": "Nyligen inloggade IPn",
"online": "Online", "online": "Online",
"away": "Borta", "away": "Borta",
"dnd": "Stör ej", "dnd": "Stör ej",

@ -1,6 +1,6 @@
{ {
"title": "Notiser", "title": "Notiser",
"no_notifs": "You have no new notifications", "no_notifs": "Du har inga nya notiser",
"see_all": "Visa alla notiser", "see_all": "Visa alla notiser",
"back_to_home": "Tillbaka till NodeBB", "back_to_home": "Tillbaka till NodeBB",
"outgoing_link": "Utgående länk", "outgoing_link": "Utgående länk",

@ -1,14 +1,14 @@
{ {
"home": "Hem", "home": "Hem",
"unread": "Olästa ämnen", "unread": "Olästa ämnen",
"popular": "Popular Topics", "popular": "Populära ämnen",
"recent": "Senaste ämnena", "recent": "Senaste ämnena",
"users": "Registrerade användare", "users": "Registrerade användare",
"notifications": "Notiser", "notifications": "Notiser",
"user.edit": "Ändrar \"%1\"", "user.edit": "Ändrar \"%1\"",
"user.following": "Personer %1 Följer", "user.following": "Personer %1 Följer",
"user.followers": "Personer som följer %1", "user.followers": "Personer som följer %1",
"user.posts": "Posts made by %1", "user.posts": "Inlägg skapat av %1",
"user.favourites": "%1's favorit-inlägg", "user.favourites": "%1's favorit-inlägg",
"user.settings": "Avnändarinställningar" "user.settings": "Avnändarinställningar"
} }

@ -11,7 +11,7 @@
"reply": "Svara", "reply": "Svara",
"edit": "Ändra", "edit": "Ändra",
"delete": "Ta bort", "delete": "Ta bort",
"restore": "Restore", "restore": "Återställ",
"move": "Flytta", "move": "Flytta",
"fork": "Grena", "fork": "Grena",
"banned": "bannad", "banned": "bannad",
@ -19,17 +19,17 @@
"share": "Dela", "share": "Dela",
"tools": "Verktyg", "tools": "Verktyg",
"flag": "Rapportera", "flag": "Rapportera",
"bookmark_instructions": "Click here to return to your last position or close to discard.", "bookmark_instructions": "Klicka här för att återgå till den senaste positionen eller stäng för att kasta.",
"flag_title": "Rapportera detta inlägg för granskning", "flag_title": "Rapportera detta inlägg för granskning",
"deleted_message": "Denna tråd har tagits bort. Endast användare med administrations-rättigheter kan se den.", "deleted_message": "Denna tråd har tagits bort. Endast användare med administrations-rättigheter kan se den.",
"following_topic.title": "Following Topic", "following_topic.title": "Följer ämne",
"following_topic.message": "You will now be receiving notifications when somebody posts to this topic.", "following_topic.message": "Du kommer nu få notiser när någon gör inlägg i detta ämne.",
"not_following_topic.title": "Not Following Topic", "not_following_topic.title": "Du följer inte ämnet",
"not_following_topic.message": "You will no longer receive notifications from this topic.", "not_following_topic.message": "Du kommer inte längre få notiser från detta ämne.",
"login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "login_to_subscribe": "Var god registrera eller logga in för att kunna prenumerera på detta ämne.",
"markAsUnreadForAll.success": "Topic marked as unread for all.", "markAsUnreadForAll.success": "Ämne markerat som oläst av alla.",
"watch": "Watch", "watch": "Följ",
"share_this_post": "Share this Post", "share_this_post": "Dela detta inlägg",
"thread_tools.title": "Trådverktyg", "thread_tools.title": "Trådverktyg",
"thread_tools.markAsUnreadForAll": "Markera som oläst", "thread_tools.markAsUnreadForAll": "Markera som oläst",
"thread_tools.pin": "Fäst ämne", "thread_tools.pin": "Fäst ämne",
@ -66,17 +66,17 @@
"composer.title_placeholder": "Skriv in ämnets titel här...", "composer.title_placeholder": "Skriv in ämnets titel här...",
"composer.write": "Skriv", "composer.write": "Skriv",
"composer.preview": "Förhandsgranska", "composer.preview": "Förhandsgranska",
"composer.help": "Help", "composer.help": "Hlp",
"composer.discard": "Avbryt", "composer.discard": "Avbryt",
"composer.submit": "Spara", "composer.submit": "Spara",
"composer.replying_to": "Svarar till", "composer.replying_to": "Svarar till",
"composer.new_topic": "Nytt ämne", "composer.new_topic": "Nytt ämne",
"composer.uploading": "uploading...", "composer.uploading": "laddar upp...",
"composer.thumb_url_label": "Paste a topic thumbnail URL", "composer.thumb_url_label": "Klistra in URL till tumnagel för ämnet",
"composer.thumb_title": "Add a thumbnail to this topic", "composer.thumb_title": "Lägg till tumnagel för detta ämne",
"composer.thumb_url_placeholder": "http://example.com/thumb.png", "composer.thumb_url_placeholder": "http://example.com/thumb.png",
"composer.thumb_file_label": "Or upload a file", "composer.thumb_file_label": "Eller ladda upp en fil",
"composer.thumb_remove": "Clear fields", "composer.thumb_remove": "Töm fält",
"composer.drag_and_drop_images": "Drag and Drop Images Here", "composer.drag_and_drop_images": "Dra och släpp bilder här",
"composer.upload_instructions": "Upload images by dragging & dropping them." "composer.upload_instructions": "Ladda upp bilder genom att dra och släpp dem."
} }

@ -19,20 +19,20 @@
"signature": "Signatur", "signature": "Signatur",
"gravatar": "Gravatar", "gravatar": "Gravatar",
"birthday": "Födelsedag", "birthday": "Födelsedag",
"chat": "Chat", "chat": "Chatta",
"follow": "Follow", "follow": "Följ",
"unfollow": "Unfollow", "unfollow": "Sluta följ",
"change_picture": "Ändra bild", "change_picture": "Ändra bild",
"edit": "Ändra", "edit": "Ändra",
"uploaded_picture": "Uppladdad bild", "uploaded_picture": "Uppladdad bild",
"upload_new_picture": "Ladda upp ny bild", "upload_new_picture": "Ladda upp ny bild",
"current_password": "Current Password", "current_password": "Nuvarande lösenord",
"change_password": "Ändra lösenord", "change_password": "Ändra lösenord",
"confirm_password": "Bekräfta lösenord", "confirm_password": "Bekräfta lösenord",
"password": "Lösenord", "password": "Lösenord",
"upload_picture": "Ladda upp bild", "upload_picture": "Ladda upp bild",
"upload_a_picture": "Ladda upp en bild", "upload_a_picture": "Ladda upp en bild",
"image_spec": "You may only upload PNG, JPG, or GIF files", "image_spec": "Du får bara ladda upp PNG, JPG eller GIF-filer",
"max": "max.", "max": "max.",
"settings": "Inställningar", "settings": "Inställningar",
"show_email": "Visa min epost", "show_email": "Visa min epost",
@ -41,7 +41,7 @@
"has_no_posts": "Denna användare har inte gjort några inlägg än.", "has_no_posts": "Denna användare har inte gjort några inlägg än.",
"email_hidden": "Epost dold", "email_hidden": "Epost dold",
"hidden": "dold", "hidden": "dold",
"paginate_description": "Paginate topics and posts instead of using infinite scroll.", "paginate_description": "Gör så att ämnen och inlägg visas som sidor istället för oändlig scroll.",
"topics_per_page": "Topics per Page", "topics_per_page": "Ämnen per sida",
"posts_per_page": "Posts per Page" "posts_per_page": "Inlägg per sida"
} }

@ -49,7 +49,7 @@
"posted": "发布", "posted": "发布",
"in": "在", "in": "在",
"recentposts": "最新发表", "recentposts": "最新发表",
"recentips": "Recently Logged In IPs", "recentips": "最近登录ip",
"online": " 在线", "online": " 在线",
"away": "离开", "away": "离开",
"dnd": "不打扰", "dnd": "不打扰",

@ -8,7 +8,7 @@
"user.edit": "编辑 \"%1\"", "user.edit": "编辑 \"%1\"",
"user.following": "%1的人关注", "user.following": "%1的人关注",
"user.followers": "%1关注的人", "user.followers": "%1关注的人",
"user.posts": "Posts made by %1", "user.posts": "%1 发表",
"user.favourites": "%1 喜爱的帖子", "user.favourites": "%1 喜爱的帖子",
"user.settings": "用户设置" "user.settings": "用户设置"
} }

@ -11,7 +11,7 @@
"reply": "回复", "reply": "回复",
"edit": "编辑", "edit": "编辑",
"delete": "删除", "delete": "删除",
"restore": "Restore", "restore": "恢复",
"move": "移动", "move": "移动",
"fork": "作为主题", "fork": "作为主题",
"banned": "禁止", "banned": "禁止",
@ -19,15 +19,15 @@
"share": "分享", "share": "分享",
"tools": "工具", "tools": "工具",
"flag": "标志", "flag": "标志",
"bookmark_instructions": "Click here to return to your last position or close to discard.", "bookmark_instructions": "点击这里返回你最初的位置或退出。",
"flag_title": "标志受限的帖子", "flag_title": "标志受限的帖子",
"deleted_message": "这个帖子已经删除,只有帖子的拥有者才有权限去查看。", "deleted_message": "这个帖子已经删除,只有帖子的拥有者才有权限去查看。",
"following_topic.title": "关注该主题", "following_topic.title": "关注该主题",
"following_topic.message": "当有回复提交的时候你将会收到通知。", "following_topic.message": "当有回复提交的时候你将会收到通知。",
"not_following_topic.title": "非关注主题", "not_following_topic.title": "非关注主题",
"not_following_topic.message": "你将不再接受来自该帖子的通知。", "not_following_topic.message": "你将不再接受来自该帖子的通知。",
"login_to_subscribe": "Please register or log in in order to subscribe to this topic.", "login_to_subscribe": "请注册或登录以订阅该主题。",
"markAsUnreadForAll.success": "Topic marked as unread for all.", "markAsUnreadForAll.success": "标记所有未读主题",
"watch": "查看", "watch": "查看",
"share_this_post": "分享帖子", "share_this_post": "分享帖子",
"thread_tools.title": "管理工具", "thread_tools.title": "管理工具",
@ -66,17 +66,17 @@
"composer.title_placeholder": "在这里输入你的主题标题...", "composer.title_placeholder": "在这里输入你的主题标题...",
"composer.write": "书写", "composer.write": "书写",
"composer.preview": "预览", "composer.preview": "预览",
"composer.help": "Help", "composer.help": "帮助",
"composer.discard": "丢弃", "composer.discard": "丢弃",
"composer.submit": "提交", "composer.submit": "提交",
"composer.replying_to": "回复", "composer.replying_to": "回复",
"composer.new_topic": "新主题", "composer.new_topic": "新主题",
"composer.uploading": "uploading...", "composer.uploading": "上传中...",
"composer.thumb_url_label": "Paste a topic thumbnail URL", "composer.thumb_url_label": "粘贴一个主题缩略图URL地址",
"composer.thumb_title": "Add a thumbnail to this topic", "composer.thumb_title": "为主题添加一个缩略图",
"composer.thumb_url_placeholder": "http://example.com/thumb.png", "composer.thumb_url_placeholder": "http://example.com/thumb.png",
"composer.thumb_file_label": "Or upload a file", "composer.thumb_file_label": "或上传一个文件",
"composer.thumb_remove": "Clear fields", "composer.thumb_remove": "清除字段",
"composer.drag_and_drop_images": "把图像拖到此处", "composer.drag_and_drop_images": "把图像拖到此处",
"composer.upload_instructions": "拖拽图片以上传" "composer.upload_instructions": "拖拽图片以上传"
} }

@ -7,7 +7,7 @@ define(function() {
successEl = $('#success'), successEl = $('#success'),
errorTextEl = errorEl.find('p'); errorTextEl = errorEl.find('p');
$('#reset').onclick = function() { $('#reset').on('click', function() {
if (inputEl.val() && inputEl.val().indexOf('@') !== -1) { if (inputEl.val() && inputEl.val().indexOf('@') !== -1) {
socket.emit('user.reset.send', { socket.emit('user.reset.send', {
email: inputEl.val() email: inputEl.val()
@ -26,7 +26,7 @@ define(function() {
errorEl.removeClass('hide').show(); errorEl.removeClass('hide').show();
errorTextEl.html('Please enter a valid email'); errorTextEl.html('Please enter a valid email');
} }
}; });
}; };
return ResetPassword; return ResetPassword;

@ -33,9 +33,8 @@ define(function() {
$('#success').removeClass('hide').addClass('show').show(); $('#success').removeClass('hide').addClass('show').show();
}); });
} }
}, false); });
// Enable the form if the code is valid
socket.emit('user.reset.valid', { socket.emit('user.reset.valid', {
code: reset_code code: reset_code
}, function(err, valid) { }, function(err, valid) {
@ -44,7 +43,7 @@ define(function() {
} }
if (valid) { if (valid) {
resetEl.disabled = false; resetEl.prop('disabled', false);
} else { } else {
var formEl = $('#reset-form'); var formEl = $('#reset-form');
// Show error message // Show error message

@ -613,7 +613,8 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
'event:topic_deleted', 'event:topic_restored', 'event:topic:locked', 'event:topic_deleted', 'event:topic_restored', 'event:topic:locked',
'event:topic_unlocked', 'event:topic_pinned', 'event:topic_unpinned', 'event:topic_unlocked', 'event:topic_pinned', 'event:topic_unpinned',
'event:topic_moved', 'event:post_edited', 'event:post_deleted', 'event:post_restored', 'event:topic_moved', 'event:post_edited', 'event:post_deleted', 'event:post_restored',
'posts.favourite', 'user.isOnline', 'posts.upvote', 'posts.downvote' 'posts.favourite', 'user.isOnline', 'posts.upvote', 'posts.downvote',
'event:topic.replyStart', 'event:topic.replyStop'
]); ]);
socket.on('get_users_in_room', function(data) { socket.on('get_users_in_room', function(data) {
@ -622,17 +623,9 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
var activeEl = $('li.post-bar[data-index="0"] .thread_active_users'); var activeEl = $('li.post-bar[data-index="0"] .thread_active_users');
function createUserIcon(uid, picture, userslug, username) { function createUserIcon(uid, picture, userslug, username) {
if(!activeEl.find('[data-uid="' + uid + '"]').length) {
if(!activeEl.find('[href="'+ RELATIVE_PATH +'/user/' + data.users[i].userslug + '"]').length) { var div = $('<div class="inline-block"><a data-uid="' + uid + '" href="' + RELATIVE_PATH + '/user/' + userslug + '"><img src="'+ picture +'"/></a></div>');
var userIcon = $('<img src="'+ picture +'"/>'); div.find('a').tooltip({
var userLink = $('<a href="' + RELATIVE_PATH + '/user/' + userslug + '"></a>').append(userIcon);
userLink.attr('data-uid', uid);
var div = $('<div class="inline-block"></div>');
div.append(userLink);
userLink.tooltip({
placement: 'top', placement: 'top',
title: username title: username
}); });
@ -642,16 +635,17 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
} }
// remove users that are no longer here // remove users that are no longer here
activeEl.children().each(function(index, element) { activeEl.find('a').each(function(index, element) {
if(element) { if(element) {
var uid = $(element).attr('data-uid'); var uid = $(element).attr('data-uid');
for(var i=0; i<data.users.length; ++i) { absent = data.users.every(function(user) {
if(data.users[i].uid == uid) { return parseInt(user.uid, 10) !== parseInt(uid, 10);
return; });
}
} if (absent) {
$(element).remove(); $(element).remove();
} }
}
}); });
var i=0; var i=0;
@ -695,6 +689,17 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
title: title title: title
}); });
} }
// Get users who are currently replying to the topic entered
socket.emit('modules.composer.getUsersByTid', templates.get('topic_id'), function(err, uids) {
var activeUsersEl = $('.thread_active_users'),
x;
if (uids && uids.length) {
for(var x=0;x<uids.length;x++) {
activeUsersEl.find('[data-uid="' + uids[x] + '"]').addClass('replying');
}
}
});
} }
app.populateOnlineUsers(); app.populateOnlineUsers();
@ -876,6 +881,14 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
} }
}); });
socket.on('event:topic.replyStart', function(uid) {
$('.thread_active_users [data-uid="' + uid + '"]').addClass('replying');
});
socket.on('event:topic.replyStop', function(uid) {
$('.thread_active_users [data-uid="' + uid + '"]').removeClass('replying');
});
function adjust_rep(value, pid, uid) { function adjust_rep(value, pid, uid) {
var votes = $('li[data-pid="' + pid + '"] .votes'), var votes = $('li[data-pid="' + pid + '"] .votes'),
reputationElements = $('.reputation[data-uid="' + uid + '"]'), reputationElements = $('.reputation[data-uid="' + uid + '"]'),
@ -887,7 +900,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
votes.html(currentVotes).attr('data-votes', currentVotes); votes.html(currentVotes).attr('data-votes', currentVotes);
reputationElements.html(reputation).attr('data-reputation', reputation); reputationElements.html(reputation).attr('data-reputation', reputation);
} };
function adjust_favourites(value, pid, uid) { function adjust_favourites(value, pid, uid) {
var favourites = $('li[data-pid="' + pid + '"] .favouriteCount'), var favourites = $('li[data-pid="' + pid + '"] .favouriteCount'),
@ -896,7 +909,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
currentFavourites += value; currentFavourites += value;
favourites.html(currentFavourites).attr('data-favourites', currentFavourites); favourites.html(currentFavourites).attr('data-favourites', currentFavourites);
} };
function set_follow_state(state, alert) { function set_follow_state(state, alert) {
@ -911,7 +924,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
type: 'success' type: 'success'
}); });
} }
} };
function set_locked_state(locked, alert) { function set_locked_state(locked, alert) {
translator.translate('<i class="fa fa-fw fa-' + (locked ? 'un': '') + 'lock"></i> [[topic:thread_tools.' + (locked ? 'un': '') + 'lock]]', function(translated) { translator.translate('<i class="fa fa-fw fa-' + (locked ? 'un': '') + 'lock"></i> [[topic:thread_tools.' + (locked ? 'un': '') + 'lock]]', function(translated) {
@ -934,7 +947,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
} }
thread_state.locked = locked ? '1' : '0'; thread_state.locked = locked ? '1' : '0';
} };
function set_delete_state(deleted) { function set_delete_state(deleted) {
var threadEl = $('#post-container'); var threadEl = $('#post-container');
@ -951,7 +964,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
} else { } else {
$('#thread-deleted').remove(); $('#thread-deleted').remove();
} }
} };
function set_pinned_state(pinned, alert) { function set_pinned_state(pinned, alert) {
translator.translate('<i class="fa fa-fw fa-thumb-tack"></i> [[topic:thread_tools.' + (pinned ? 'unpin' : 'pin') + ']]', function(translated) { translator.translate('<i class="fa fa-fw fa-thumb-tack"></i> [[topic:thread_tools.' + (pinned ? 'unpin' : 'pin') + ']]', function(translated) {
@ -968,7 +981,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
} }
thread_state.pinned = pinned ? '1' : '0'; thread_state.pinned = pinned ? '1' : '0';
}); });
} };
function toggle_post_delete_state(pid) { function toggle_post_delete_state(pid) {
var postEl = $('#post-container li[data-pid="' + pid + '"]'); var postEl = $('#post-container li[data-pid="' + pid + '"]');
@ -980,7 +993,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
updatePostCount(); updatePostCount();
} }
} };
function toggle_post_tools(pid, isDeleted) { function toggle_post_tools(pid, isDeleted) {
var postEl = $('#post-container li[data-pid="' + pid + '"]'); var postEl = $('#post-container li[data-pid="' + pid + '"]');
@ -990,7 +1003,7 @@ define(['composer', 'forum/pagination'], function(composer, pagination) {
translator.translate(isDeleted ? ' [[topic:restore]]' : ' [[topic:delete]]', function(translated) { translator.translate(isDeleted ? ' [[topic:restore]]' : ' [[topic:delete]]', function(translated) {
postEl.find('.delete').find('span').html(translated); postEl.find('.delete').find('span').html(translated);
}); });
} };
$(window).on('scroll', updateHeader); $(window).on('scroll', updateHeader);
$(window).trigger('action:topic.loaded'); $(window).trigger('action:topic.loaded');

@ -4,6 +4,15 @@ define(['taskbar'], function(taskbar) {
posts: {} posts: {}
}; };
function initialise() {
socket.on('event:composer.ping', function(post_uuid) {
if (composer.active === post_uuid) {
socket.emit('modules.composer.pingActive', post_uuid);
}
});
};
initialise();
function maybeParse(response) { function maybeParse(response) {
if (typeof response == 'string') { if (typeof response == 'string') {
try { try {
@ -380,6 +389,16 @@ define(['taskbar'], function(taskbar) {
} else { } else {
composer.createNewComposer(post_uuid); composer.createNewComposer(post_uuid);
} }
var postData = composer.posts[post_uuid];
if (postData.tid) {
// Replying to a topic
socket.emit('modules.composer.register', {
uuid: post_uuid,
tid: postData.tid,
uid: app.uid
});
}
}; };
composer.createNewComposer = function(post_uuid) { composer.createNewComposer = function(post_uuid) {
@ -813,6 +832,8 @@ define(['taskbar'], function(taskbar) {
taskbar.discard('composer', post_uuid); taskbar.discard('composer', post_uuid);
$('body').css({'margin-bottom': 0}); $('body').css({'margin-bottom': 0});
$('.action-bar button').removeAttr('disabled'); $('.action-bar button').removeAttr('disabled');
socket.emit('modules.composer.unregister', post_uuid);
} }
}; };
@ -821,6 +842,8 @@ define(['taskbar'], function(taskbar) {
postContainer.css('visibility', 'hidden'); postContainer.css('visibility', 'hidden');
composer.active = undefined; composer.active = undefined;
taskbar.minimize('composer', post_uuid); taskbar.minimize('composer', post_uuid);
socket.emit('modules.composer.unregister', post_uuid);
}; };
return { return {

@ -1,3 +1,6 @@
'use strict';
var db = require('./database'), var db = require('./database'),
posts = require('./posts'), posts = require('./posts'),
utils = require('./../public/src/utils'), utils = require('./../public/src/utils'),
@ -13,7 +16,6 @@ var db = require('./database'),
nconf = require('nconf'); nconf = require('nconf');
(function(Categories) { (function(Categories) {
"use strict";
Categories.create = function(data, callback) { Categories.create = function(data, callback) {
db.incrObjectField('global', 'nextCid', function(err, cid) { db.incrObjectField('global', 'nextCid', function(err, cid) {
@ -102,12 +104,13 @@ var db = require('./database'),
}); });
} }
var indices = {}; var indices = {},
for(var i=0; i<tids.length; ++i) { i = 0;
for(i=0; i<tids.length; ++i) {
indices[tids[i]] = start + i; indices[tids[i]] = start + i;
} }
for(var i=0; i<topics.length; ++i) { for(i=0; i<topics.length; ++i) {
topics[i].index = indices[topics[i].tid]; topics[i].index = indices[topics[i].tid];
} }
@ -161,11 +164,11 @@ var db = require('./database'),
Categories.getAllCategories = function(uid, callback) { Categories.getAllCategories = function(uid, callback) {
db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) { db.getSortedSetRange('categories:cid', 0, -1, function(err, cids) {
if(err) { if (err) {
return callback(err); return callback(err);
} }
if(cids && cids.length === 0) { if (!cids || (cids && cids.length === 0)) {
return callback(null, {categories : []}); return callback(null, {categories : []});
} }
@ -339,7 +342,6 @@ var db = require('./database'),
'categories': categories 'categories': categories
}); });
}); });
}; };
Categories.isUserActiveIn = function(cid, uid, callback) { Categories.isUserActiveIn = function(cid, uid, callback) {

@ -1,7 +1,8 @@
'use strict';
(function(module) { (function(module) {
'use strict';
var winston = require('winston'), var winston = require('winston'),
async = require('async'), async = require('async'),
nconf = require('nconf'), nconf = require('nconf'),
@ -68,15 +69,15 @@
}); });
if(typeof callback === 'function') { if(typeof callback === 'function') {
callback(null); callback();
} }
} }
}); });
} };
module.close = function() { module.close = function() {
db.close(); db.close();
} };
// //
// helper functions // helper functions
@ -129,7 +130,7 @@
winston.error('Error indexing ' + err.message); winston.error('Error indexing ' + err.message);
} }
}); });
} };
module.search = function(key, term, limit, callback) { module.search = function(key, term, limit, callback) {
db.command({text:"search" , search: term, filter: {key:key}, limit: limit }, function(err, result) { db.command({text:"search" , search: term, filter: {key:key}, limit: limit }, function(err, result) {
@ -150,7 +151,7 @@
callback(null, []); callback(null, []);
} }
}); });
} };
module.searchRemove = function(key, id, callback) { module.searchRemove = function(key, id, callback) {
db.collection('search').remove({id:id, key:key}, function(err, result) { db.collection('search').remove({id:id, key:key}, function(err, result) {
@ -161,27 +162,22 @@
callback(); callback();
} }
}); });
} };
module.flushdb = function(callback) { module.flushdb = function(callback) {
db.dropDatabase(function(err, result) { db.dropDatabase(function(err, result) {
if(err){ if (err) {
winston.error(error); winston.error(err.message);
if(typeof callback === 'function') { if (typeof callback === 'function') {
return callback(err); return callback(err);
} }
} }
if(typeof callback === 'function') { if (typeof callback === 'function') {
callback(null); callback();
} }
}); });
} };
module.getFileName = function(callback) {
throw new Error('not-implemented');
}
module.info = function(callback) { module.info = function(callback) {
db.stats({scale:1024}, function(err, stats) { db.stats({scale:1024}, function(err, stats) {
@ -189,10 +185,6 @@
return callback(err); return callback(err);
} }
// TODO : if this it not deleted the templates break,
// it is a nested object inside stats
delete stats.dataFileVersion;
stats.avgObjSize = (stats.avgObjSize / 1024).toFixed(2); stats.avgObjSize = (stats.avgObjSize / 1024).toFixed(2);
stats.raw = JSON.stringify(stats, null, 4); stats.raw = JSON.stringify(stats, null, 4);
@ -201,7 +193,7 @@
callback(null, stats); callback(null, stats);
}); });
} };
// key // key
@ -209,7 +201,7 @@
db.collection('objects').findOne({_key:key}, function(err, item) { db.collection('objects').findOne({_key:key}, function(err, item) {
callback(err, item !== undefined && item !== null); callback(err, item !== undefined && item !== null);
}); });
} };
module.delete = function(key, callback) { module.delete = function(key, callback) {
db.collection('objects').remove({_key:key}, function(err, result) { db.collection('objects').remove({_key:key}, function(err, result) {
@ -217,22 +209,22 @@
callback(err, result); callback(err, result);
} }
}); });
} };
module.get = function(key, callback) { module.get = function(key, callback) {
module.getObjectField(key, 'value', callback); module.getObjectField(key, 'value', callback);
} };
module.set = function(key, value, callback) { module.set = function(key, value, callback) {
var data = {value:value}; var data = {value:value};
module.setObject(key, data, callback); module.setObject(key, data, callback);
} };
module.keys = function(key, callback) { module.keys = function(key, callback) {
db.collection('objects').find( { _key: { $regex: key /*, $options: 'i'*/ } }, function(err, result) { db.collection('objects').find( { _key: { $regex: key /*, $options: 'i'*/ } }, function(err, result) {
callback(err, result); callback(err, result);
}); });
} };
module.rename = function(oldKey, newKey, callback) { module.rename = function(oldKey, newKey, callback) {
db.collection('objects').update({_key: oldKey}, {$set:{_key: newKey}}, function(err, result) { db.collection('objects').update({_key: oldKey}, {$set:{_key: newKey}}, function(err, result) {
@ -240,25 +232,25 @@
callback(err, result); callback(err, result);
} }
}); });
} };
module.expire = function(key, seconds, callback) { module.expire = function(key, seconds, callback) {
module.expireAt(key, Math.round(Date.now() / 1000) + seconds, callback); module.expireAt(key, Math.round(Date.now() / 1000) + seconds, callback);
} };
module.expireAt = function(key, timestamp, callback) { module.expireAt = function(key, timestamp, callback) {
module.setObjectField(key, 'expireAt', new Date(timestamp * 1000), callback); module.setObjectField(key, 'expireAt', new Date(timestamp * 1000), callback);
} };
//hashes //hashes
module.setObject = function(key, data, callback) { module.setObject = function(key, data, callback) {
data['_key'] = key; data._key = key;
db.collection('objects').update({_key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) { db.collection('objects').update({_key:key}, {$set:data}, {upsert:true, w: 1}, function(err, result) {
if(typeof callback === 'function') { if(typeof callback === 'function') {
callback(err, result); callback(err, result);
} }
}); });
} };
module.setObjectField = function(key, field, value, callback) { module.setObjectField = function(key, field, value, callback) {
var data = {}; var data = {};
@ -273,7 +265,7 @@
callback(err, result); callback(err, result);
} }
}); });
} };
module.getObject = function(key, callback) { module.getObject = function(key, callback) {
db.collection('objects').findOne({_key:key}, function(err, item) { db.collection('objects').findOne({_key:key}, function(err, item) {
@ -281,7 +273,7 @@
callback(err, item); callback(err, item);
}); });
} };
module.getObjects = function(keys, callback) { module.getObjects = function(keys, callback) {
@ -299,7 +291,7 @@
callback(null, returnData); callback(null, returnData);
}); });
} };
module.getObjectField = function(key, field, callback) { module.getObjectField = function(key, field, callback) {
module.getObjectFields(key, [field], function(err, data) { module.getObjectFields(key, [field], function(err, data) {
@ -309,7 +301,7 @@
callback(null, data[field]); callback(null, data[field]);
}); });
} };
module.getObjectFields = function(key, fields, callback) { module.getObjectFields = function(key, fields, callback) {
@ -342,7 +334,7 @@
callback(null, item); callback(null, item);
}); });
} };
module.getObjectKeys = function(key, callback) { module.getObjectKeys = function(key, callback) {
module.getObject(key, function(err, data) { module.getObject(key, function(err, data) {
@ -356,7 +348,7 @@
callback(null, []); callback(null, []);
} }
}); });
} };
module.getObjectValues = function(key, callback) { module.getObjectValues = function(key, callback) {
module.getObject(key, function(err, data) { module.getObject(key, function(err, data) {
@ -370,7 +362,7 @@
} }
callback(null, values); callback(null, values);
}); });
} };
module.isObjectField = function(key, field, callback) { module.isObjectField = function(key, field, callback) {
var data = {}; var data = {};
@ -385,7 +377,7 @@
} }
callback(err, !!item && item[field] !== undefined && item[field] !== null); callback(err, !!item && item[field] !== undefined && item[field] !== null);
}); });
} };
module.deleteObjectField = function(key, field, callback) { module.deleteObjectField = function(key, field, callback) {
var data = {}; var data = {};
@ -399,15 +391,15 @@
callback(err, result); callback(err, result);
} }
}); });
} };
module.incrObjectField = function(key, field, callback) { module.incrObjectField = function(key, field, callback) {
module.incrObjectFieldBy(key, field, 1, callback); module.incrObjectFieldBy(key, field, 1, callback);
} };
module.decrObjectField = function(key, field, callback) { module.decrObjectField = function(key, field, callback) {
module.incrObjectFieldBy(key, field, -1, callback); module.incrObjectFieldBy(key, field, -1, callback);
} };
module.incrObjectFieldBy = function(key, field, value, callback) { module.incrObjectFieldBy = function(key, field, value, callback) {
var data = {}; var data = {};
@ -422,7 +414,7 @@
callback(err, result ? result[field] : null); callback(err, result ? result[field] : null);
} }
}); });
} };
// sets // sets
@ -437,19 +429,12 @@
}); });
db.collection('objects').update({_key:key}, db.collection('objects').update({_key: key}, { $addToSet: { members: { $each: value } } }, { upsert: true, w: 1 }, function(err, result) {
{
$addToSet: { members: { $each: value } }
},
{
upsert:true, w: 1
}
, function(err, result) {
if(typeof callback === 'function') { if(typeof callback === 'function') {
callback(err, result); callback(err, result);
} }
}); });
} };
module.setRemove = function(key, value, callback) { module.setRemove = function(key, value, callback) {
if(!Array.isArray(value)) { if(!Array.isArray(value)) {
@ -465,7 +450,7 @@
callback(err, result); callback(err, result);
} }
}); });
} };
module.isSetMember = function(key, value, callback) { module.isSetMember = function(key, value, callback) {
if(value !== null && value !== undefined) { if(value !== null && value !== undefined) {
@ -475,7 +460,7 @@
db.collection('objects').findOne({_key:key, members: value}, function(err, item) { db.collection('objects').findOne({_key:key, members: value}, function(err, item) {
callback(err, item !== null && item !== undefined); callback(err, item !== null && item !== undefined);
}); });
} };
module.isMemberOfSets = function(sets, value, callback) { module.isMemberOfSets = function(sets, value, callback) {
@ -498,7 +483,7 @@
callback(err, result); callback(err, result);
}); });
} };
module.getSetMembers = function(key, callback) { module.getSetMembers = function(key, callback) {
db.collection('objects').findOne({_key:key}, {members:1}, function(err, data) { db.collection('objects').findOne({_key:key}, {members:1}, function(err, data) {
@ -512,7 +497,7 @@
callback(null, data.members); callback(null, data.members);
} }
}); });
} };
module.setCount = function(key, callback) { module.setCount = function(key, callback) {
db.collection('objects').findOne({_key:key}, function(err, data) { db.collection('objects').findOne({_key:key}, function(err, data) {
@ -525,7 +510,7 @@
callback(null, data.members.length); callback(null, data.members.length);
}); });
} };
module.setRemoveRandom = function(key, callback) { module.setRemoveRandom = function(key, callback) {
db.collection('objects').findOne({_key:key}, function(err, data) { db.collection('objects').findOne({_key:key}, function(err, data) {
@ -551,7 +536,7 @@
}); });
} }
}); });
} };
// sorted sets // sorted sets
@ -570,7 +555,7 @@
callback(err, result); callback(err, result);
} }
}); });
} };
module.sortedSetRemove = function(key, value, callback) { module.sortedSetRemove = function(key, value, callback) {
if(value !== null && value !== undefined) { if(value !== null && value !== undefined) {
@ -582,7 +567,7 @@
callback(err, result); callback(err, result);
} }
}); });
} };
function getSortedSetRange(key, start, stop, sort, callback) { function getSortedSetRange(key, start, stop, sort, callback) {
db.collection('objects').find({_key:key}, {fields:{value:1}}) db.collection('objects').find({_key:key}, {fields:{value:1}})
@ -590,26 +575,29 @@
.skip(start) .skip(start)
.sort({score: sort}) .sort({score: sort})
.toArray(function(err, data) { .toArray(function(err, data) {
if(err) { if (err) {
return callback(err); return callback(err);
} }
// maybe this can be done with mongo? if (!data) {
return callback(null, null);
}
data = data.map(function(item) { data = data.map(function(item) {
return item.value; return item.value;
}); });
callback(err, data); callback(null, data);
}); });
} }
module.getSortedSetRange = function(key, start, stop, callback) { module.getSortedSetRange = function(key, start, stop, callback) {
getSortedSetRange(key, start, stop, 1, callback); getSortedSetRange(key, start, stop, 1, callback);
} };
module.getSortedSetRevRange = function(key, start, stop, callback) { module.getSortedSetRevRange = function(key, start, stop, callback) {
getSortedSetRange(key, start, stop, -1, callback); getSortedSetRange(key, start, stop, -1, callback);
} };
module.getSortedSetRevRangeByScore = function(args, callback) { module.getSortedSetRevRangeByScore = function(args, callback) {
@ -640,7 +628,7 @@
callback(err, data); callback(err, data);
}); });
} };
module.sortedSetCount = function(key, min, max, callback) { module.sortedSetCount = function(key, min, max, callback) {
db.collection('objects').count({_key:key, score: {$gte:min, $lte:max}}, function(err, count) { db.collection('objects').count({_key:key, score: {$gte:min, $lte:max}}, function(err, count) {
@ -653,7 +641,7 @@
} }
callback(null,count); callback(null,count);
}); });
} };
module.sortedSetCard = function(key, callback) { module.sortedSetCard = function(key, callback) {
db.collection('objects').count({_key:key}, function(err, count) { db.collection('objects').count({_key:key}, function(err, count) {
@ -666,7 +654,7 @@
} }
callback(null, count); callback(null, count);
}); });
} };
module.sortedSetRank = function(key, value, callback) { module.sortedSetRank = function(key, value, callback) {
if(value !== null && value !== undefined) { if(value !== null && value !== undefined) {
@ -683,7 +671,7 @@
callback(null, rank); callback(null, rank);
}); });
} };
module.sortedSetRevRank = function(key, value, callback) { module.sortedSetRevRank = function(key, value, callback) {
if(value !== null && value !== undefined) { if(value !== null && value !== undefined) {
@ -702,7 +690,7 @@
callback(null, rank); callback(null, rank);
}); });
} };
module.sortedSetScore = function(key, value, callback) { module.sortedSetScore = function(key, value, callback) {
if(value !== null && value !== undefined) { if(value !== null && value !== undefined) {
@ -718,13 +706,13 @@
callback(err, null); callback(err, null);
}); });
} };
module.isSortedSetMember = function(key, value, callback) { module.isSortedSetMember = function(key, value, callback) {
module.sortedSetScore(key, value, function(err, score) { module.sortedSetScore(key, value, function(err, score) {
callback(err, !!score); callback(err, !!score);
}); });
} };
module.sortedSetsScore = function(keys, value, callback) { module.sortedSetsScore = function(keys, value, callback) {
if(value !== null && value !== undefined) { if(value !== null && value !== undefined) {
@ -745,13 +733,14 @@
callback(null, returnData); callback(null, returnData);
}); });
} };
// lists // lists
module.listPrepend = function(key, value, callback) { module.listPrepend = function(key, value, callback) {
if(value !== null && value !== undefined) { if(value !== null && value !== undefined) {
value = value.toString(); value = value.toString();
} }
module.isObjectField(key, 'array', function(err, exists) { module.isObjectField(key, 'array', function(err, exists) {
if(err) { if(err) {
if(typeof callback === 'function') { if(typeof callback === 'function') {
@ -770,9 +759,8 @@
} else { } else {
module.listAppend(key, value, callback); module.listAppend(key, value, callback);
} }
});
}) };
}
module.listAppend = function(key, value, callback) { module.listAppend = function(key, value, callback) {
if(value !== null && value !== undefined) { if(value !== null && value !== undefined) {
@ -783,7 +771,7 @@
callback(err, result); callback(err, result);
} }
}); });
} };
module.listRemoveLast = function(key, callback) { module.listRemoveLast = function(key, callback) {
module.getListRange(key, -1, 0, function(err, value) { module.getListRange(key, -1, 0, function(err, value) {
@ -808,7 +796,7 @@
} }
}); });
}); });
} };
module.listRemoveAll = function(key, value, callback) { module.listRemoveAll = function(key, value, callback) {
if(value !== null && value !== undefined) { if(value !== null && value !== undefined) {
@ -820,7 +808,7 @@
callback(err, result); callback(err, result);
} }
}); });
} };
module.getListRange = function(key, start, stop, callback) { module.getListRange = function(key, start, stop, callback) {
@ -867,8 +855,7 @@
callback(null, []); callback(null, []);
} }
}); });
} };
}(exports)); }(exports));

@ -1,16 +1,19 @@
'use strict';
(function(module) { (function(module) {
'use strict';
var winston = require('winston'), var winston = require('winston'),
nconf = require('nconf'), nconf = require('nconf'),
path = require('path'),
express = require('express'), express = require('express'),
redis_socket_or_host = nconf.get('redis:host'), redis_socket_or_host = nconf.get('redis:host'),
utils = require('./../../public/src/utils.js'), utils = require('./../../public/src/utils.js'),
redis, redis,
connectRedis, connectRedis,
reds, reds,
redisClient; redisClient,
postSearch,
topicSearch;
try { try {
redis = require('redis'); redis = require('redis');
@ -21,7 +24,7 @@
process.exit(); process.exit();
} }
module.init = function(callback) {
if (redis_socket_or_host && redis_socket_or_host.indexOf('/')>=0) { if (redis_socket_or_host && redis_socket_or_host.indexOf('/')>=0) {
/* If redis.host contains a path name character, use the unix dom sock connection. ie, /tmp/redis.sock */ /* If redis.host contains a path name character, use the unix dom sock connection. ie, /tmp/redis.sock */
redisClient = redis.createClient(nconf.get('redis:host')); redisClient = redis.createClient(nconf.get('redis:host'));
@ -41,7 +44,6 @@
process.exit(); process.exit();
}); });
module.client = redisClient; module.client = redisClient;
module.sessionStore = new connectRedis({ module.sessionStore = new connectRedis({
@ -53,13 +55,12 @@
return reds.client || (reds.client = redisClient); return reds.client || (reds.client = redisClient);
}; };
var userSearch = reds.createSearch('nodebbusersearch'),
postSearch = reds.createSearch('nodebbpostsearch'), postSearch = reds.createSearch('nodebbpostsearch'),
topicSearch = reds.createSearch('nodebbtopicsearch'); topicSearch = reds.createSearch('nodebbtopicsearch');
var db = parseInt(nconf.get('redis:database'), 10); var db = parseInt(nconf.get('redis:database'), 10);
if (db){ if (db) {
redisClient.select(db, function(error) { redisClient.select(db, function(error) {
if(error) { if(error) {
winston.error("NodeBB could not connect to your Redis database. Redis returned the following error: " + error.message); winston.error("NodeBB could not connect to your Redis database. Redis returned the following error: " + error.message);
@ -68,26 +69,23 @@
}); });
} }
module.init = function(callback) { callback();
callback(null); };
}
module.close = function() { module.close = function() {
redisClient.quit(); redisClient.quit();
} };
// //
// Exported functions // Exported functions
// //
module.searchIndex = function(key, content, id) { module.searchIndex = function(key, content, id) {
if(key === 'post') { if (key === 'post') {
postSearch.index(content, id); postSearch.index(content, id);
} else if(key === 'topic') { } else if(key === 'topic') {
topicSearch.index(content, id); topicSearch.index(content, id);
} else if(key === 'user') {
userSearch.index(content, id);
}
} }
};
module.search = function(key, term, limit, callback) { module.search = function(key, term, limit, callback) {
function search(searchObj, callback) { function search(searchObj, callback) {
@ -102,55 +100,30 @@
search(postSearch, callback); search(postSearch, callback);
} else if(key === 'topic') { } else if(key === 'topic') {
search(topicSearch, callback); search(topicSearch, callback);
} else if(key === 'user') {
search(userSearch, callback);
}
} }
};
module.searchRemove = function(key, id, callback) { module.searchRemove = function(key, id, callback) {
if(key === 'post') { if(key === 'post') {
postSearch.remove(id); postSearch.remove(id);
} else if(key === 'topic') { } else if(key === 'topic') {
topicSearch.remove(id); topicSearch.remove(id);
} else if(key === 'user') {
userSearch.remove(id);
} }
if (typeof callback === 'function') { if (typeof callback === 'function') {
callback() callback();
}
} }
};
module.flushdb = function(callback) { module.flushdb = function(callback) {
redisClient.send_command('flushdb', [], function(err) { redisClient.send_command('flushdb', [], function(err) {
if(err){
winston.error(error);
return callback(err);
}
callback(null);
});
}
module.getFileName = function(callback) {
var multi = redisClient.multi();
multi.config('get', 'dir');
multi.config('get', 'dbfilename');
multi.exec(function (err, results) {
if (err) { if (err) {
winston.error(err.message);
return callback(err); return callback(err);
} }
callback();
results = results.reduce(function (memo, config) {
memo[config[0]] = config[1];
return memo;
}, {});
var dbFile = path.join(results.dir, results.dbfilename);
callback(null, dbFile);
}); });
} };
module.info = function(callback) { module.info = function(callback) {
redisClient.info(function (err, data) { redisClient.info(function (err, data) {
@ -172,7 +145,7 @@
callback(null, redisData); callback(null, redisData);
}); });
} };
// key // key
@ -180,35 +153,35 @@
redisClient.exists(key, function(err, exists) { redisClient.exists(key, function(err, exists) {
callback(err, exists === 1); callback(err, exists === 1);
}); });
} };
module.delete = function(key, callback) { module.delete = function(key, callback) {
redisClient.del(key, callback); redisClient.del(key, callback);
} };
module.get = function(key, callback) { module.get = function(key, callback) {
redisClient.get(key, callback); redisClient.get(key, callback);
} };
module.set = function(key, value, callback) { module.set = function(key, value, callback) {
redisClient.set(key, value, callback); redisClient.set(key, value, callback);
} };
module.keys = function(key, callback) { module.keys = function(key, callback) {
redisClient.keys(key, callback); redisClient.keys(key, callback);
} };
module.rename = function(oldKey, newKey, callback) { module.rename = function(oldKey, newKey, callback) {
redisClient.rename(oldKey, newKey, callback); redisClient.rename(oldKey, newKey, callback);
} };
module.expire = function(key, seconds, callback) { module.expire = function(key, seconds, callback) {
redisClient.expire(key, seconds, callback); redisClient.expire(key, seconds, callback);
} };
module.expireAt = function(key, timestamp, callback) { module.expireAt = function(key, timestamp, callback) {
redisClient.expireat(key, timestamp, callback); redisClient.expireat(key, timestamp, callback);
} };
//hashes //hashes
@ -219,15 +192,15 @@
callback(err, res); callback(err, res);
} }
}); });
} };
module.setObjectField = function(key, field, value, callback) { module.setObjectField = function(key, field, value, callback) {
redisClient.hset(key, field, value, callback); redisClient.hset(key, field, value, callback);
} };
module.getObject = function(key, callback) { module.getObject = function(key, callback) {
redisClient.hgetall(key, callback); redisClient.hgetall(key, callback);
} };
module.getObjects = function(keys, callback) { module.getObjects = function(keys, callback) {
var multi = redisClient.multi(); var multi = redisClient.multi();
@ -239,7 +212,7 @@
multi.exec(function (err, replies) { multi.exec(function (err, replies) {
callback(err, replies); callback(err, replies);
}); });
} };
module.getObjectField = function(key, field, callback) { module.getObjectField = function(key, field, callback) {
module.getObjectFields(key, [field], function(err, data) { module.getObjectFields(key, [field], function(err, data) {
@ -249,7 +222,7 @@
callback(null, data[field]); callback(null, data[field]);
}); });
} };
module.getObjectFields = function(key, fields, callback) { module.getObjectFields = function(key, fields, callback) {
redisClient.hmget(key, fields, function(err, data) { redisClient.hmget(key, fields, function(err, data) {
@ -265,48 +238,48 @@
callback(null, returnData); callback(null, returnData);
}); });
} };
module.getObjectKeys = function(key, callback) { module.getObjectKeys = function(key, callback) {
redisClient.hkeys(key, callback); redisClient.hkeys(key, callback);
} };
module.getObjectValues = function(key, callback) { module.getObjectValues = function(key, callback) {
redisClient.hvals(key, callback); redisClient.hvals(key, callback);
} };
module.isObjectField = function(key, field, callback) { module.isObjectField = function(key, field, callback) {
redisClient.hexists(key, field, function(err, exists) { redisClient.hexists(key, field, function(err, exists) {
callback(err, exists === 1); callback(err, exists === 1);
}); });
} };
module.deleteObjectField = function(key, field, callback) { module.deleteObjectField = function(key, field, callback) {
redisClient.hdel(key, field, callback); redisClient.hdel(key, field, callback);
} };
module.incrObjectField = function(key, field, callback) { module.incrObjectField = function(key, field, callback) {
redisClient.hincrby(key, field, 1, callback); redisClient.hincrby(key, field, 1, callback);
} };
module.decrObjectField = function(key, field, callback) { module.decrObjectField = function(key, field, callback) {
redisClient.hincrby(key, field, -1, callback); redisClient.hincrby(key, field, -1, callback);
} };
module.incrObjectFieldBy = function(key, field, value, callback) { module.incrObjectFieldBy = function(key, field, value, callback) {
redisClient.hincrby(key, field, value, callback); redisClient.hincrby(key, field, value, callback);
} };
// sets // sets
module.setAdd = function(key, value, callback) { module.setAdd = function(key, value, callback) {
redisClient.sadd(key, value, callback); redisClient.sadd(key, value, callback);
} };
module.setRemove = function(key, value, callback) { module.setRemove = function(key, value, callback) {
redisClient.srem(key, value, callback); redisClient.srem(key, value, callback);
} };
module.isSetMember = function(key, value, callback) { module.isSetMember = function(key, value, callback) {
redisClient.sismember(key, value, function(err, result) { redisClient.sismember(key, value, function(err, result) {
@ -316,7 +289,7 @@
callback(null, result === 1); callback(null, result === 1);
}); });
} };
module.isMemberOfSets = function(sets, value, callback) { module.isMemberOfSets = function(sets, value, callback) {
var batch = redisClient.multi(); var batch = redisClient.multi();
@ -326,67 +299,67 @@
} }
batch.exec(callback); batch.exec(callback);
} };
module.getSetMembers = function(key, callback) { module.getSetMembers = function(key, callback) {
redisClient.smembers(key, callback); redisClient.smembers(key, callback);
} };
module.setCount = function(key, callback) { module.setCount = function(key, callback) {
redisClient.scard(key, callback); redisClient.scard(key, callback);
} };
module.setRemoveRandom = function(key, callback) { module.setRemoveRandom = function(key, callback) {
redisClient.spop(key, callback); redisClient.spop(key, callback);
} };
// sorted sets // sorted sets
module.sortedSetAdd = function(key, score, value, callback) { module.sortedSetAdd = function(key, score, value, callback) {
redisClient.zadd(key, score, value, callback); redisClient.zadd(key, score, value, callback);
} };
module.sortedSetRemove = function(key, value, callback) { module.sortedSetRemove = function(key, value, callback) {
redisClient.zrem(key, value, callback); redisClient.zrem(key, value, callback);
} };
module.getSortedSetRange = function(key, start, stop, callback) { module.getSortedSetRange = function(key, start, stop, callback) {
redisClient.zrange(key, start, stop, callback); redisClient.zrange(key, start, stop, callback);
} };
module.getSortedSetRevRange = function(key, start, stop, callback) { module.getSortedSetRevRange = function(key, start, stop, callback) {
redisClient.zrevrange(key, start, stop, callback); redisClient.zrevrange(key, start, stop, callback);
} };
module.getSortedSetRevRangeByScore = function(args, callback) { module.getSortedSetRevRangeByScore = function(args, callback) {
redisClient.zrevrangebyscore(args, callback); redisClient.zrevrangebyscore(args, callback);
} };
module.sortedSetCount = function(key, min, max, callback) { module.sortedSetCount = function(key, min, max, callback) {
redisClient.zcount(key, min, max, callback); redisClient.zcount(key, min, max, callback);
} };
module.sortedSetCard = function(key, callback) { module.sortedSetCard = function(key, callback) {
redisClient.zcard(key, callback); redisClient.zcard(key, callback);
} };
module.sortedSetRank = function(key, value, callback) { module.sortedSetRank = function(key, value, callback) {
redisClient.zrank(key, value, callback); redisClient.zrank(key, value, callback);
} };
module.sortedSetRevRank = function(key, value, callback) { module.sortedSetRevRank = function(key, value, callback) {
redisClient.zrevrank(key, value, callback); redisClient.zrevrank(key, value, callback);
} };
module.sortedSetScore = function(key, value, callback) { module.sortedSetScore = function(key, value, callback) {
redisClient.zscore(key, value, callback); redisClient.zscore(key, value, callback);
} };
module.isSortedSetMember = function(key, value, callback) { module.isSortedSetMember = function(key, value, callback) {
module.sortedSetScore(key, value, function(err, score) { module.sortedSetScore(key, value, function(err, score) {
callback(err, !!score); callback(err, !!score);
}); });
} };
module.sortedSetsScore = function(keys, value, callback) { module.sortedSetsScore = function(keys, value, callback) {
var multi = redisClient.multi(); var multi = redisClient.multi();
@ -396,31 +369,28 @@
} }
multi.exec(callback); multi.exec(callback);
} };
// lists // lists
module.listPrepend = function(key, value, callback) { module.listPrepend = function(key, value, callback) {
redisClient.lpush(key, value, callback); redisClient.lpush(key, value, callback);
} };
module.listAppend = function(key, value, callback) { module.listAppend = function(key, value, callback) {
redisClient.rpush(key, value, callback); redisClient.rpush(key, value, callback);
} };
module.listRemoveLast = function(key, callback) { module.listRemoveLast = function(key, callback) {
redisClient.rpop(key, callback); redisClient.rpop(key, callback);
} };
module.listRemoveAll = function(key, value, callback) { module.listRemoveAll = function(key, value, callback) {
redisClient.lrem(key, 0, value, callback); redisClient.lrem(key, 0, value, callback);
} };
module.getListRange = function(key, start, stop, callback) { module.getListRange = function(key, start, stop, callback) {
redisClient.lrange(key, start, stop, callback); redisClient.lrange(key, start, stop, callback);
} };
}(exports)); }(exports));

@ -297,12 +297,6 @@ var fs = require('fs'),
} }
}; };
Meta.db = {
getFile: function (callback) {
db.getFileName(callback);
}
};
Meta.css = { Meta.css = {
cache: undefined cache: undefined
}; };

@ -1,3 +1,5 @@
'use strict';
var winston = require('winston'), var winston = require('winston'),
async = require('async'), async = require('async'),
nconf = require('nconf'), nconf = require('nconf'),
@ -14,15 +16,20 @@ var winston = require('winston'),
meta = require('./meta'); meta = require('./meta');
(function(PostTools) { (function(PostTools) {
PostTools.isMain = function(pid, tid, callback) { PostTools.isMain = function(pid, tid, callback) {
db.getSortedSetRange('tid:' + tid + ':posts', 0, 0, function(err, pids) { db.getSortedSetRange('tid:' + tid + ':posts', 0, 0, function(err, pids) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
if(!Array.isArray(pids) || !pids.length) {
callback(null, false);
}
callback(null, parseInt(pids[0], 10) === parseInt(pid, 10)); callback(null, parseInt(pids[0], 10) === parseInt(pid, 10));
}); });
} };
PostTools.privileges = function(pid, uid, callback) { PostTools.privileges = function(pid, uid, callback) {
async.parallel({ async.parallel({
@ -48,7 +55,6 @@ var winston = require('winston'),
}); });
} }
} }
// [getThreadPrivileges, isOwnPost, hasEnoughRep]
}, function(err, results) { }, function(err, results) {
if(err) { if(err) {
return callback(err); return callback(err);
@ -61,26 +67,29 @@ var winston = require('winston'),
move: results.topicPrivs.admin || results.topicPrivs.moderator move: results.topicPrivs.admin || results.topicPrivs.moderator
}); });
}); });
} };
PostTools.edit = function(uid, pid, title, content, options) { PostTools.edit = function(uid, pid, title, content, options, callback) {
options || (options = {}); options = options || {};
var websockets = require('./socket.io'), function success(postData) {
success = function() {
posts.setPostFields(pid, { posts.setPostFields(pid, {
edited: Date.now(), edited: Date.now(),
editor: uid, editor: uid,
content: content content: postData.content
}); });
events.logPostEdit(uid, pid); events.logPostEdit(uid, pid);
async.parallel([ async.parallel({
function(next) { topic: function(next) {
posts.getPostField(pid, 'tid', function(err, tid) { var tid = postData.tid;
PostTools.isMain(pid, tid, function(err, isMainPost) { PostTools.isMain(pid, tid, function(err, isMainPost) {
if (err) {
return next(err);
}
if (isMainPost) { if (isMainPost) {
title = title.trim(); title = title.trim();
var slug = tid + '/' + utils.slugify(title); var slug = tid + '/' + utils.slugify(title);
@ -93,40 +102,44 @@ var winston = require('winston'),
plugins.fireHook('action:topic.edit', tid); plugins.fireHook('action:topic.edit', tid);
} }
posts.getPostData(pid, function(err, postData) {
plugins.fireHook('action:post.edit', postData); plugins.fireHook('action:post.edit', postData);
});
next(null, { next(null, {
tid: tid, tid: tid,
title: validator.escape(title),
isMainPost: isMainPost isMainPost: isMainPost
}); });
}); });
});
}, },
function(next) { content: function(next) {
PostTools.parse(content, next); PostTools.parse(postData.content, next);
}
}, callback);
} }
], function(err, results) {
websockets.in('topic_' + results[0].tid).emit('event:post_edited', {
pid: pid,
title: validator.escape(title),
isMainPost: results[0].isMainPost,
content: results[1]
});
});
};
PostTools.privileges(pid, uid, function(err, privileges) { PostTools.privileges(pid, uid, function(err, privileges) {
if (privileges.editable) { if (err || !privileges.editable) {
plugins.fireHook('filter:post.save', content, function(err, parsedContent) { return callback(err || new Error('not-privileges-to-edit'));
if (!err) content = parsedContent;
success();
});
} }
});
posts.getPostData(pid, function(err, postData) {
if (err) {
return callback(err);
} }
postData.content = content;
plugins.fireHook('filter:post.save', postData, function(err, postData) {
if (err) {
return callback(err);
}
success(postData);
});
});
});
};
PostTools.delete = function(uid, pid, callback) { PostTools.delete = function(uid, pid, callback) {
var success = function() { var success = function() {
posts.setPostField(pid, 'deleted', 1); posts.setPostField(pid, 'deleted', 1);
@ -183,7 +196,7 @@ var winston = require('winston'),
} }
}); });
}); });
} };
PostTools.restore = function(uid, pid, callback) { PostTools.restore = function(uid, pid, callback) {
var success = function() { var success = function() {
@ -238,7 +251,7 @@ var winston = require('winston'),
} }
}); });
}); });
} };
PostTools.parse = function(raw, callback) { PostTools.parse = function(raw, callback) {
raw = raw || ''; raw = raw || '';
@ -246,7 +259,7 @@ var winston = require('winston'),
plugins.fireHook('filter:post.parse', raw, function(err, parsed) { plugins.fireHook('filter:post.parse', raw, function(err, parsed) {
callback(null, !err ? parsed : raw); callback(null, !err ? parsed : raw);
}); });
} };
PostTools.parseSignature = function(raw, callback) { PostTools.parseSignature = function(raw, callback) {
raw = raw || ''; raw = raw || '';
@ -254,5 +267,6 @@ var winston = require('winston'),
plugins.fireHook('filter:post.parseSignature', raw, function(err, parsedSignature) { plugins.fireHook('filter:post.parseSignature', raw, function(err, parsedSignature) {
callback(null, !err ? parsedSignature : raw); callback(null, !err ? parsedSignature : raw);
}); });
} };
}(exports)); }(exports));

@ -29,35 +29,26 @@ var db = require('./database'),
toPid = data.toPid; toPid = data.toPid;
if (uid === null) { if (uid === null) {
return callback(new Error('invalid-user'), null); return callback(new Error('invalid-user'));
} }
var timestamp = Date.now(),
postData;
async.waterfall([ async.waterfall([
function(next) { function(next) {
topics.isLocked(tid, next);
},
function(locked, next) {
if(locked) {
return next(new Error('topic-locked'));
}
db.incrObjectField('global', 'nextPid', next); db.incrObjectField('global', 'nextPid', next);
}, },
function(pid, next) { function(pid, next) {
plugins.fireHook('filter:post.save', content, function(err, newContent) {
next(err, pid, newContent);
});
},
function(pid, newContent, next) {
var timestamp = Date.now(),
postData = { postData = {
'pid': pid, 'pid': pid,
'uid': uid, 'uid': uid,
'tid': tid, 'tid': tid,
'content': newContent, 'content': content,
'timestamp': timestamp, 'timestamp': timestamp,
'reputation': '0', 'reputation': 0,
'votes': '0', 'votes': 0,
'editor': '', 'editor': '',
'edited': 0, 'edited': 0,
'deleted': 0 'deleted': 0
@ -67,23 +58,20 @@ var db = require('./database'),
postData.toPid = toPid; postData.toPid = toPid;
} }
db.setObject('post:' + pid, postData, function(err) { plugins.fireHook('filter:post.save', postData, next);
if(err) { },
return next(err); function(postData, next) {
} db.setObject('post:' + postData.pid, postData, next);
},
db.sortedSetAdd('posts:pid', timestamp, pid); function(result, next) {
db.sortedSetAdd('posts:pid', timestamp, postData.pid);
db.incrObjectField('global', 'postCount'); db.incrObjectField('global', 'postCount');
topics.onNewPostMade(tid, pid, timestamp); topics.onNewPostMade(tid, postData.pid, timestamp);
categories.onNewPostMade(uid, tid, pid, timestamp); categories.onNewPostMade(uid, tid, postData.pid, timestamp);
user.onNewPostMade(uid, tid, pid, timestamp); user.onNewPostMade(uid, tid, postData.pid, timestamp);
next(null, postData);
});
},
function(postData, next) {
plugins.fireHook('filter:post.get', postData, next); plugins.fireHook('filter:post.get', postData, next);
}, },
function(postData, next) { function(postData, next) {
@ -103,36 +91,34 @@ var db = require('./database'),
}; };
Posts.getPostsByTid = function(tid, start, end, reverse, callback) { Posts.getPostsByTid = function(tid, start, end, reverse, callback) {
if (typeof reverse === 'function') {
callback = reverse;
reverse = false;
}
db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange']('tid:' + tid + ':posts', start, end, function(err, pids) { db[reverse ? 'getSortedSetRevRange' : 'getSortedSetRange']('tid:' + tid + ':posts', start, end, function(err, pids) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
if(!pids.length) { if(!Array.isArray(pids) || !pids.length) {
return callback(null, []); return callback(null, []);
} }
plugins.fireHook('filter:post.getTopic', pids, function(err, posts) { Posts.getPostsByPids(pids, function(err, posts) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
if(!posts.length) { if(!Array.isArray(posts) || !posts.length) {
return callback(null, []); return callback(null, []);
} }
plugins.fireHook('filter:post.getPosts', {tid: tid, posts: posts}, function(err, data) {
Posts.getPostsByPids(pids, function(err, posts) {
if(err) { if(err) {
return callback(err); return callback(err);
} }
plugins.fireHook('action:post.gotTopic', posts);
callback(null, posts); if(!data || !Array.isArray(data.posts)) {
return callback(null, []);
}
callback(null, data.posts);
}); });
}); });
}); });

@ -347,28 +347,6 @@ var nconf = require('nconf'),
res.json(data); res.json(data);
}); });
}); });
// app.get('/export', function (req, res) {
// meta.db.getFile(function (err, dbFile) {
// if (!err) {
// res.download(dbFile, 'redis.rdb', function (err) {
// console.log(err);
// res.send(500);
// if (err) {
// res.send(500);
// switch (err.code) {
// case 'EACCES':
// res.send(500, 'Require permissions from Redis database file: ', dbFile);
// break;
// default:
// res.send(500);
// break;
// }
// }
// });
// } else res.send(500);
// });
// });
}); });
app.get('/events', function(req, res, next) { app.get('/events', function(req, res, next) {

@ -235,7 +235,11 @@ function isUserOnline(uid) {
Sockets.updateRoomBrowsingText = updateRoomBrowsingText; Sockets.updateRoomBrowsingText = updateRoomBrowsingText;
function updateRoomBrowsingText(roomName) { function updateRoomBrowsingText(roomName) {
function getUidsInRoom(room) { if (!roomName) {
return;
}
function getUidsInRoom() {
var uids = []; var uids = [];
var clients = io.sockets.clients(roomName); var clients = io.sockets.clients(roomName);
for(var i=0; i<clients.length; ++i) { for(var i=0; i<clients.length; ++i) {
@ -246,7 +250,7 @@ function updateRoomBrowsingText(roomName) {
return uids; return uids;
} }
function getAnonymousCount(roomName) { function getAnonymousCount() {
var clients = io.sockets.clients(roomName); var clients = io.sockets.clients(roomName);
var anonCount = 0; var anonCount = 0;
@ -258,8 +262,10 @@ function updateRoomBrowsingText(roomName) {
return anonCount; return anonCount;
} }
var uids = getUidsInRoom(roomName), var uids = getUidsInRoom(),
anonymousCount = getAnonymousCount(roomName); anonymousCount = getAnonymousCount();
user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture', 'status'], function(err, users) { user.getMultipleUserFields(uids, ['uid', 'username', 'userslug', 'picture', 'status'], function(err, users) {
if(!err) { if(!err) {

@ -85,7 +85,7 @@ SocketMeta.rooms.enter = function(socket, data) {
socket.join(data.enter); socket.join(data.enter);
if (data.leave) { if (data.leave && data.leave !== data.enter) {
module.parent.exports.updateRoomBrowsingText(data.leave); module.parent.exports.updateRoomBrowsingText(data.leave);
} }

@ -12,13 +12,37 @@ var posts = require('../posts'),
async = require('async'), async = require('async'),
S = require('string'), S = require('string'),
winston = require('winston'), winston = require('winston'),
_ = require('underscore'),
server = require('./'), server = require('./'),
SocketModules = {}; SocketModules = {};
/* Posts Composer */ /* Posts Composer */
SocketModules.composer = {}; SocketModules.composer = {
replyHash: {}
};
var stopTracking = function(replyObj) {
if (isLast(replyObj.uid, replyObj.tid)) {
server.in('topic_' + replyObj.tid).emit('event:topic.replyStop', replyObj.uid);
}
clearInterval(replyObj.timer);
delete SocketModules.composer.replyHash[replyObj.uuid];
},
isLast = function(uid, tid) {
return _.filter(SocketModules.composer.replyHash, function(replyObj, uuid) {
if (
parseInt(replyObj.tid, 10) === parseInt(tid, 10) &&
parseInt(replyObj.uid, 10) === parseInt(uid, 10)
) {
return true;
} else {
return false;
}
}).length === 1;
};
SocketModules.composer.push = function(socket, pid, callback) { SocketModules.composer.push = function(socket, pid, callback) {
if (socket.uid || parseInt(meta.config.allowGuestPosting, 10)) { if (socket.uid || parseInt(meta.config.allowGuestPosting, 10)) {
@ -74,6 +98,50 @@ SocketModules.composer.renderHelp = function(socket, data, callback) {
plugins.fireHook('filter:composer.help', '', callback); plugins.fireHook('filter:composer.help', '', callback);
}; };
SocketModules.composer.register = function(socket, data) {
var now = Date.now();
server.in('topic_' + data.tid).emit('event:topic.replyStart', data.uid);
data.socket = socket;
data.lastPing = now;
data.lastAnswer = now;
data.timer = setInterval(function() {
if (data.lastPing === data.lastAnswer) {
// Ping the socket to see if the composer is still active
data.lastPing = Date.now();
socket.emit('event:composer.ping', data.uuid);
} else {
stopTracking(data);
}
}, 1000*5); // Every 5 seconds...
SocketModules.composer.replyHash[data.uuid] = data;
};
SocketModules.composer.unregister = function(socket, uuid) {
var replyObj = SocketModules.composer.replyHash[uuid];
if (uuid && replyObj) {
stopTracking(replyObj);
}
};
SocketModules.composer.pingActive = function(socket, uuid) {
var data = SocketModules.composer.replyHash[uuid];
if (data) {
data.lastAnswer = data.lastPing;
}
};
SocketModules.composer.getUsersByTid = function(socket, tid, callback) {
// Return uids with active composers
callback(null, _.filter(SocketModules.composer.replyHash, function(replyObj, uuid) {
return parseInt(replyObj.tid, 10) === parseInt(tid, 10);
}).map(function(replyObj) {
return replyObj.uid
}));
}
/* Chat */ /* Chat */
SocketModules.chats = {}; SocketModules.chats = {};

@ -151,8 +151,20 @@ SocketPosts.edit = function(socket, data, callback) {
return callback(new Error('content-too-short')); return callback(new Error('content-too-short'));
} }
postTools.edit(socket.uid, data.pid, data.title, data.content, {topic_thumb: data.topic_thumb}); postTools.edit(socket.uid, data.pid, data.title, data.content, {topic_thumb: data.topic_thumb}, function(err, results) {
if(err) {
return callback(err);
}
index.server.sockets.in('topic_' + results.topic.tid).emit('event:post_edited', {
pid: data.pid,
title: results.topic.title,
isMainPost: results.topic.isMainPost,
content: results.content
});
callback(); callback();
});
}; };
SocketPosts.delete = function(socket, data, callback) { SocketPosts.delete = function(socket, data, callback) {

@ -171,6 +171,14 @@ var async = require('async'),
if (!topicExists) { if (!topicExists) {
return next(new Error('topic doesn\'t exist')); return next(new Error('topic doesn\'t exist'));
} }
Topics.isLocked(tid, next);
},
function(locked, next) {
if (locked) {
return next(new Error('topic-locked'));
}
threadTools.privileges(tid, uid, next); threadTools.privileges(tid, uid, next);
}, },
function(privilegesData, next) { function(privilegesData, next) {

Loading…
Cancel
Save