From: Greg Burri Date: Sat, 21 Jun 2008 10:01:06 +0000 (+0000) Subject: MOD passage de quelques fichier de iso-8859-1 à UTF8 X-Git-Tag: 1.1.0~120 X-Git-Url: http://git.euphorik.ch/?p=euphorik.git;a=commitdiff_plain;h=2424ba818818a6bd0b547fa540742f9b54db990b MOD passage de quelques fichier de iso-8859-1 à UTF8 ADD dossier de projet pour netbeans (erlybird) --- diff --git a/doc/description.txt b/doc/description.txt index 11e1205..94eee1a 100644 --- a/doc/description.txt +++ b/doc/description.txt @@ -1,112 +1,112 @@ -== En bref == -Euphorik est un site web communautaire principalement basé sur un système d'échange de messages instantanés. -Attention: la description ici ne correspond pas à l'état actuel du projet mais à un but à atteindre. - -== Philosophie == -Euphorik est un site communaire de niveau supérieur (un truc qui n'existe pas et qui n'existera probablement jamais). -N'importe qui peut poster des messages ou des trolls (un troll étant un super message persistant à caractère trollifique). -Il est possible de s'enregistrer pour garder son identité et sauvegarder certains paramètres. -Pas besoin d'être authentifier pour poster des messages -Il faut être enregistré pour poster des trolls (ouais bein quant on troll on assume) -Il n'y a qu'un seul canal par troll (channel au sens IRC). -Un message peut répondre à un ou plusieurs autres messages, ceci crée automatiquement des arbres de conversation (au sein d'un troll). -Ces arbres de conversation peuvent être extraient de la conversation principal et affichés séparement (toujours au sein d'un troll). -Il existe des êtres supérieures qui ont de grands pouvoirs, ce sont les EkMaster ou [EM] (les admins quoi). -L'interface doit être sobre, simple et un peu retro :) -Il est interdit d'utiliser des technos pourries comme PHP. - - -== Détails == -=== Pages === -* Main : Présente les trolls de la semaine -* Trolls : Liste un certain nombre de trolls postés par les utilisateurs. Le rafraichissement est en temps réel. Il est possible de faire une recherche par mot clef. -* People : Permet de rechercher une personne et d'afficher sa page, en particulier ses trolls. -* Profile : Permet d'accèder à ses données. C'est à partir de cette page que l'on peut poster des trolls. -* About : description du site (Faq et cie..) - -=== Le Troll === -Le troll est un message, une question, une pensée, etc, digne d'intérêt (ou pas) étant la fusion entre un topic de forum et un channel de chat. -Il existe un troll principal concernant le chat principal. -Un troll peut être édité par son auteur. -N'importe qui peut voir l'historique des éditions. -Il est possible de plusser ou moinsser un troll. -Un troll possède de 0 à n tag (mot-clef). -Les trolls sont présentés au sein d'une liste général ordrée en fonction de leur nombre de point et de leur date et aussi tant qu'on y est de la date du dernier message (genre reddit.com) -Les trolls sont également présentés sur le profile du proprio du troll (par ordre anti-chronologique) - -=== Le troll de la semaine === -Sur la page principale appelé 'chat' il existe un troll qui sera affiché une semaine appellé "troll de la semaine". -Le troll de la semaine est posté par les admins. -Les admins voient les prochains trolls en attente, le nombre en attente est limité 10. -Un admin peut ajouter un troll de la semaine. Il ne peut pas posséder plus d'un troll en attente. -Le troll de la semaine change le lundi à 3h00 du mat' s'il en existe un en attente. Il est choisi au hasard. -Les n derniers trolls des semaines précédentes sont toujours affichés de manière repliés en dessous du troll de la semaine. pour l'instant n = 4. - -=== Le message === -Un message répond à un troll et peut répondre à d'autres messages de ce troll. -Un message ne peut pas être éditer, il est possible de lui appondre une ou plusieurs corrections " +++ Correction" -un message dont l'entête est de couleur verte signfie : "un message qui me répond" -un message dont l'entête est de couleur orange signifie : "mon message" -Un message dont l'entête est de couleur bleu signifie : "un message auquel je répond" - - -=== Admin === -L'admin propose des trolls de la semaine, il a le statut de EM (EkMaster) - - -== Reflexions == -Les types d'information du plus éphémère au plus persistant. - * Plussage/moinssage - * Message - * Message (1-1) - * Blog - * Forum - * Article - * Question (1-1) | (1-n) - * Forum - * Message - * Billet (1-n) - * Blog - * Article (1-n) | (n-n) - * Wikipedia - - -Moyen de communication sur le net : - -* Réseaux sociaux (facebook et cie) - + Orienté profile - + Liste d'amis - + Possibilité de mettre des infos personnels + photos - -* Vidéo (youtube et cie) - + Orienté vidéo - -* Reddit/Digg - * Aggrégateur de news/billet de blog/article - * L'ordre des informations peut changer (en fonction de la note) - -* Blog - * Orienté billet - * Géré par une seule personne - * Système de messages - + Structuration et recherche par tag (chaque billet possède un ou plusieurs tags) - -* Forum (phpBB, vBulettin, mesDiscussions, etc.) - * Orienté sujet - * Organisation hiérachique en thémes, p.e. : Hardware/HDD - * L'ordre des sujets ne correspond pas à leur date d'écriture mais à la date du dernier message - * Edition/correction possible - * Les "réponses" ne sont pas modérer par l'auteur du sujet - - Pas de système de plussage - - Par forcément d'arbre de réponses, obligation de quoter -> bordelique - - Le topic a souvent tendance à dériver - -* Chat (http://www.phpfreechat.net, http://bouchot.org, etc..) - * Orienté message - * Ordre figé - + Scalable grace aux channels - + Communication temps réel - - Ca peut devenir le bordel, difficile de suivre - - L'information est éphemère ou difficilement réutilisable - - Aucune hiérarchie ou structure en dehors des channels +== En bref == +Euphorik est un site web communautaire principalement basé sur un système d'échange de messages instantanés. +Attention: la description ici ne correspond pas à l'état actuel du projet mais à un but à atteindre. + +== Philosophie == +Euphorik est un site communaire de niveau supérieur (un truc qui n'existe pas et qui n'existera probablement jamais). +N'importe qui peut poster des messages ou des trolls (un troll étant un super message persistant à caractère trollifique). +Il est possible de s'enregistrer pour garder son identité et sauvegarder certains paramètres. +Pas besoin d'être authentifier pour poster des messages +Il faut être enregistré pour poster des trolls (ouais bein quant on troll on assume) +Il n'y a qu'un seul canal par troll (channel au sens IRC). +Un message peut répondre à un ou plusieurs autres messages, ceci crée automatiquement des arbres de conversation (au sein d'un troll). +Ces arbres de conversation peuvent être extraient de la conversation principal et affichés séparement (toujours au sein d'un troll). +Il existe des êtres supérieures qui ont de grands pouvoirs, ce sont les EkMaster ou [EM] (les admins quoi). +L'interface doit être sobre, simple et un peu retro :) +Il est interdit d'utiliser des technos pourries comme PHP. + + +== Détails == +=== Pages === +* Main : Présente les trolls de la semaine +* Trolls : Liste un certain nombre de trolls postés par les utilisateurs. Le rafraichissement est en temps réel. Il est possible de faire une recherche par mot clef. +* People : Permet de rechercher une personne et d'afficher sa page, en particulier ses trolls. +* Profile : Permet d'accèder à ses données. C'est à partir de cette page que l'on peut poster des trolls. +* About : description du site (Faq et cie..) + +=== Le Troll === +Le troll est un message, une question, une pensée, etc, digne d'intérêt (ou pas) étant la fusion entre un topic de forum et un channel de chat. +Il existe un troll principal concernant le chat principal. +Un troll peut être édité par son auteur. +N'importe qui peut voir l'historique des éditions. +Il est possible de plusser ou moinsser un troll. +Un troll possède de 0 à n tag (mot-clef). +Les trolls sont présentés au sein d'une liste général ordrée en fonction de leur nombre de point et de leur date et aussi tant qu'on y est de la date du dernier message (genre reddit.com) +Les trolls sont également présentés sur le profile du proprio du troll (par ordre anti-chronologique) + +=== Le troll de la semaine === +Sur la page principale appelé 'chat' il existe un troll qui sera affiché une semaine appellé "troll de la semaine". +Le troll de la semaine est posté par les admins. +Les admins voient les prochains trolls en attente, le nombre en attente est limité 10. +Un admin peut ajouter un troll de la semaine. Il ne peut pas posséder plus d'un troll en attente. +Le troll de la semaine change le lundi à 3h00 du mat' s'il en existe un en attente. Il est choisi au hasard. +Les n derniers trolls des semaines précédentes sont toujours affichés de manière repliés en dessous du troll de la semaine. pour l'instant n = 4. + +=== Le message === +Un message répond à un troll et peut répondre à d'autres messages de ce troll. +Un message ne peut pas être éditer, il est possible de lui appondre une ou plusieurs corrections " +++ Correction" +un message dont l'entête est de couleur verte signfie : "un message qui me répond" +un message dont l'entête est de couleur orange signifie : "mon message" +Un message dont l'entête est de couleur bleu signifie : "un message auquel je répond" + + +=== Admin === +L'admin propose des trolls de la semaine, il a le statut de EM (EkMaster) + + +== Reflexions == +Les types d'information du plus éphémère au plus persistant. + * Plussage/moinssage + * Message + * Message (1-1) + * Blog + * Forum + * Article + * Question (1-1) | (1-n) + * Forum + * Message + * Billet (1-n) + * Blog + * Article (1-n) | (n-n) + * Wikipedia + + +Moyen de communication sur le net : + +* Réseaux sociaux (facebook et cie) + + Orienté profile + + Liste d'amis + + Possibilité de mettre des infos personnels + photos + +* Vidéo (youtube et cie) + + Orienté vidéo + +* Reddit/Digg + * Aggrégateur de news/billet de blog/article + * L'ordre des informations peut changer (en fonction de la note) + +* Blog + * Orienté billet + * Géré par une seule personne + * Système de messages + + Structuration et recherche par tag (chaque billet possède un ou plusieurs tags) + +* Forum (phpBB, vBulettin, mesDiscussions, etc.) + * Orienté sujet + * Organisation hiérachique en thémes, p.e. : Hardware/HDD + * L'ordre des sujets ne correspond pas à leur date d'écriture mais à la date du dernier message + * Edition/correction possible + * Les "réponses" ne sont pas modérer par l'auteur du sujet + - Pas de système de plussage + - Par forcément d'arbre de réponses, obligation de quoter -> bordelique + - Le topic a souvent tendance à dériver + +* Chat (http://www.phpfreechat.net, http://bouchot.org, etc..) + * Orienté message + * Ordre figé + + Scalable grace aux channels + + Communication temps réel + - Ca peut devenir le bordel, difficile de suivre + - L'information est éphemère ou difficilement réutilisable + - Aucune hiérarchie ou structure en dehors des channels \ No newline at end of file diff --git a/doc/installation.txt b/doc/installation.txt index 4f71285..57bb630 100644 --- a/doc/installation.txt +++ b/doc/installation.txt @@ -1,79 +1,79 @@ --- Description de l'installation de Euphorik -- - -Voici les différentes étapes décrivant l'installation du site euphorik. Certaines données sont a adaptées en fonction des besoins. L'installation est décrite pour le système d'exploitation Debian. - -* On admet que le dossier de base est "/euphorik". -* On admet que l'utilisateur courant est "toto" et qu'il possède "/euphorik" -* Tout ce qui commence par un '$' correspond à une ligne de commande tapée dans le shell de l'OS. -* Tout ce qui commence par un '>' correspond à une instruction dans le shell de erlang. - - -1. Installer Yaws - a) $apt-get install yaws - -2. Configurer Yaws - a) Ajouter les lignes suivantes dans /etc/yaws/yaws.conf : - - "ebin_dir = /euphorik/modules/ebin" - - "include_dir = /euphorik/modules/include" - b) Le serveur virtuel est définit comme ceci dans /etc/yaws/conf.d/localhost.conf : - - port = 8081 - listen = 0.0.0.0 - docroot = /euphorik - allowed_scripts = [yaws] +-- Description de l'installation de Euphorik -- + +Voici les différentes étapes décrivant l'installation du site euphorik. Certaines données sont a adaptées en fonction des besoins. L'installation est décrite pour le système d'exploitation Debian. + +* On admet que le dossier de base est "/euphorik". +* On admet que l'utilisateur courant est "toto" et qu'il possède "/euphorik" +* Tout ce qui commence par un '$' correspond à une ligne de commande tapée dans le shell de l'OS. +* Tout ce qui commence par un '>' correspond à une instruction dans le shell de erlang. + + +1. Installer Yaws + a) $apt-get install yaws + +2. Configurer Yaws + a) Ajouter les lignes suivantes dans /etc/yaws/yaws.conf : + - "ebin_dir = /euphorik/modules/ebin" + - "include_dir = /euphorik/modules/include" + b) Le serveur virtuel est définit comme ceci dans /etc/yaws/conf.d/localhost.conf : + + port = 8081 + listen = 0.0.0.0 + docroot = /euphorik + allowed_scripts = [yaws] appmods = - start_mod = euphorik_daemon - - c) Editer '/etc/init.d/yaws' et remplacer cette ligne : - script="$DAEMON -I $YAWS_ID $@" - par celle ci : - script="$DAEMON --sname yaws --mnesiadir \"/euphorik/BD\" -I $YAWS_ID $@" - FIXME : trouver une méthode plus élégante. - -3. Créer la base de donnée - a) Arreter Yaws (en root) : - $/etc/init.s/yaws stop - b) Lancer un noeud Erlang - - Se placer dans le répertoire /euphorik/modules/ebin - - Executer : - $erl -sname yaws -mnesia dir '"/euphorik/BD"' - c) Charger le module : - >l(euphorik_bd). - d) Créer la base : - >euphorik_bd:create(). - e) Démarrer Yaws (en root) : - $/etc/init.s/yaws start - -4. Adminisatration du site Euphorik - a) Connexion au noeud "yaws" - erl -sname gb - puis dans la console : - CTRL-G - r yaws@overnux - c 2 - Pour plus d'infos : http://www.ejabberd.im/interconnect-erl-nodes - Il est possible de connecter un shell directement sur le noeud de yaws comme ceci : - erl -sname gb -remsh yaws@overnux - - b) Utiliser les outils des modules - - Par exemple : - >euphorik_minichat:messages(10). - pour voir les 10 derniers messages - - c) Ancienne méthode de connexion (plus compliqué) - - Le cookie de Yaws (/var/run/yaws/.erlang.cookie ou /var/cache/yaws/.erlang.cookie) et celui de l'utilisateur courant (~/.erlang.cookie) doit être le même. - (si le cookie de yaws est modifié il faut relancer yaws). - - Se placer dans le répertoire /euphorik/modules/ebin - - Executer : - $erl -sname toto - où "toto" est le nom du noeud (tout sauf "yaws") - - Charger le module du minichat : - >l(euphorik_minichat) - - Se connecter au noeud yaws : - >euphorik_minichat:connect() - la valeur retournée doit être : {ok,[yaws@overnux]} - - d) Informations sur la mémoire consommée : - Mémoire totale (ko) : - trunc(element(2, lists:nth(1, memory())) / 1024). - voir c:i() également + start_mod = euphorik_daemon + + c) Editer '/etc/init.d/yaws' et remplacer cette ligne : + script="$DAEMON -I $YAWS_ID $@" + par celle ci : + script="$DAEMON --sname yaws --mnesiadir \"/euphorik/BD\" -I $YAWS_ID $@" + FIXME : trouver une méthode plus élégante. + +3. Créer la base de donnée + a) Arreter Yaws (en root) : + $/etc/init.s/yaws stop + b) Lancer un noeud Erlang + - Se placer dans le répertoire /euphorik/modules/ebin + - Executer : + $erl -sname yaws -mnesia dir '"/euphorik/BD"' + c) Charger le module : + >l(euphorik_bd). + d) Créer la base : + >euphorik_bd:create(). + e) Démarrer Yaws (en root) : + $/etc/init.s/yaws start + +4. Adminisatration du site Euphorik + a) Connexion au noeud "yaws" + erl -sname gb + puis dans la console : + CTRL-G + r yaws@overnux + c 2 + Pour plus d'infos : http://www.ejabberd.im/interconnect-erl-nodes + Il est possible de connecter un shell directement sur le noeud de yaws comme ceci : + erl -sname gb -remsh yaws@overnux + + b) Utiliser les outils des modules + - Par exemple : + >euphorik_minichat:messages(10). + pour voir les 10 derniers messages + + c) Ancienne méthode de connexion (plus compliqué) + - Le cookie de Yaws (/var/run/yaws/.erlang.cookie ou /var/cache/yaws/.erlang.cookie) et celui de l'utilisateur courant (~/.erlang.cookie) doit être le même. + (si le cookie de yaws est modifié il faut relancer yaws). + - Se placer dans le répertoire /euphorik/modules/ebin + - Executer : + $erl -sname toto + où "toto" est le nom du noeud (tout sauf "yaws") + - Charger le module du minichat : + >l(euphorik_minichat) + - Se connecter au noeud yaws : + >euphorik_minichat:connect() + la valeur retournée doit être : {ok,[yaws@overnux]} + + d) Informations sur la mémoire consommée : + Mémoire totale (ko) : + trunc(element(2, lists:nth(1, memory())) / 1024). + voir c:i() également diff --git a/doc/protocole3.txt b/doc/protocole3.txt index 8c8d253..e89b357 100644 --- a/doc/protocole3.txt +++ b/doc/protocole3.txt @@ -2,38 +2,38 @@ Euphorik - Protocole v3 ----------------------- == Introduction == -Ce document a pour but de décrire la communication client-serveur du site euphorik. -Les messages échangés sont basés sur le format JSON. +Ce document a pour but de décrire la communication client-serveur du site euphorik. +Les messages échangés sont basés sur le format JSON. Ce document remplace 'protocole2.txt'. - + == Principes == -Enregistrement: - * Permet de créer un compte, un cookie est donné en retour. Ce cookie doit être stocké par le client pour pouvoir s'authentifier par la suite. +Enregistrement: + * Permet de créer un compte, un cookie est donné en retour. Ce cookie doit être stocké par le client pour pouvoir s'authentifier par la suite. -Authentification: - * L'authentification (login) se fait soit par un couple soit à l'aide d'un cookie. - * Permet de récupérer les données d'un profile +Authentification: + * L'authentification (login) se fait soit par un couple soit à l'aide d'un cookie. + * Permet de récupérer les données d'un profile Rafraichissement: * Le client envoie une demande au serveur avec l'id du dernier message (via XMLHttpRequest ou un function de JQuery) - * Le serveur maintient la connexion bloquée si le client est à jour. - * Dès qu'un nouveau message arrive, le serveur débloque la connexion et envoie le ou les messages manquants. + * Le serveur maintient la connexion bloquée si le client est à jour. + * Dès qu'un nouveau message arrive, le serveur débloque la connexion et envoie le ou les messages manquants. == Protocole == c : client s : server -Les messages client vers serveur sont envoyés par HTTP-POST. +Les messages client vers serveur sont envoyés par HTTP-POST. -A toutes les requêtes le serveur peut répondre une erreur : +A toutes les requêtes le serveur peut répondre une erreur : { "reply" : "error", "error_message" : "blabla" } -Message ok générique : +Message ok générique : { "reply" : "ok" @@ -41,9 +41,9 @@ Message ok g === Enregistrement et authentification === -Permet de créer un nouvel utilisateur. -"login" et "password" peuvent ne pas être fournis avec un message de type "register", dans ce cas l'utilisateur ne pourra s'authentifier qu'a l'aide de son cookie. -Le mot de passe est hashé en md5. +Permet de créer un nouvel utilisateur. +"login" et "password" peuvent ne pas être fournis avec un message de type "register", dans ce cas l'utilisateur ne pourra s'authentifier qu'a l'aide de son cookie. +Le mot de passe est hashé en md5. c -> s { @@ -72,49 +72,49 @@ s -> c "css" : "css/1/euphorik.css", "main_page" : 1 } -ou +ou { - "reply" : "register" | "authentification", - "status" : "auth_registered", - "cookie" : "LKJDLAKSJBFLKASN", - "id" : 193, - "nick" : "Paul", - "login" : "paul49", - "email" : "paul@pierre.com", + "reply" : "register" | "authentification", + "status" : "auth_registered", + "cookie" : "LKJDLAKSJBFLKASN", + "id" : 193, + "nick" : "Paul", + "login" : "paul49", + "email" : "paul@pierre.com", "css" : "css/3/euphorik.css", "nick_format" : "nick" | "login" | "nick_login", "view_times" : true | false, - "view_tooltips" : true | false, + "view_tooltips" : true | false, // "main_page" : 1, "conversations" : [3, 8], - "ek_master" : true | false + "ek_master" : true | false } - + === Logout === -c -> s - { - "action" : "logout", - "cookie" : "LKJDLAKSJBFLKASN" +c -> s + { + "action" : "logout", + "cookie" : "LKJDLAKSJBFLKASN" } - + === Profile === -c -> s - { - "action" : "set_profile", - "cookie" : "LKJDLAKSJBFLKASN", - "login" : "paul49", - "password" : "IJKJDHHSAD9081238", - "nick" : "Paul", - "email" : "paul@pierre.com", +c -> s + { + "action" : "set_profile", + "cookie" : "LKJDLAKSJBFLKASN", + "login" : "paul49", + "password" : "IJKJDHHSAD9081238", + "nick" : "Paul", + "email" : "paul@pierre.com", "css" : "css/3/euphorik.css", "nick_format" : "nick" | "login" | "nick_login", "view_times" : true | false, - "view_tooltips" : true | false, + "view_tooltips" : true | false, "main_page" : 1, - "conversations" : [3, 8] - } + "conversations" : [3, 8] + } s -> c @@ -123,15 +123,15 @@ ou === Wait event (page = chat) === -Si "last_message_id" est absent alors le client ne possède pas de message. +Si "last_message_id" est absent alors le client ne possède pas de message. Si "main_page" est absent alors est vaut 1. "cookie" n'est pas obligatoire. -c -> s - { +c -> s + { "action" : "wait_event", - "page" : "chat" - "cookie" : "LKJDLAKSJBFLKASN", + "page" : "chat" + "cookie" : "LKJDLAKSJBFLKASN", "message_count" : 10, "last_message_id" : 163, "main_page" : 1, @@ -142,46 +142,46 @@ c -> s "page" : 1, "last_message_id" : 4 (pas obligatoire) } - ] + ] } -s -> c -La première conversation est la principale (main). -L'ordre des conversation est le même que celui des données de l'utilisateur. -Le format de la date n'est pas formel. - { - "reply" : "new_message", - "conversations" : [ - { - "last_page" : true | false, +s -> c +La première conversation est la principale (main). +L'ordre des conversation est le même que celui des données de l'utilisateur. +Le format de la date n'est pas formel. + { + "reply" : "new_message", + "conversations" : [ + { + "last_page" : true | false, "messages" : [ - { + { "id" : 54, - "user_id" : 344, - "date" : "Hier 17:26:54", - "system" : true | false, - "owner" : true | false, - "answered" : true | false, - "is_a_reply" : true | false, - "nick" : "Paul", - "login" : "paul_22", + "user_id" : 344, + "date" : "Hier 17:26:54", + "system" : true | false, + "owner" : true | false, + "answered" : true | false, + "is_a_reply" : true | false, + "nick" : "Paul", + "login" : "paul_22", "content" : "Salut", - "root" : 453, - "answer_to" : [ - { "id" : 123, "nick" : "Pierre", "login" : "pierre_45" } + "root" : 453, + "answer_to" : [ + { "id" : 123, "nick" : "Pierre", "login" : "pierre_45" } ] "ek_master" : true | false - } - ] + } + ] } - ... - ] + ... + ] } -ou - { - "reply" : "message_updated", - "message_id" : 123, - "content" : "Salut +++ poulpe" +ou + { + "reply" : "message_updated", + "message_id" : 123, + "content" : "Salut +++ poulpe" } ou { @@ -229,19 +229,19 @@ s -> c "troll_id" : 2 } ou -indique de mettre à jour la liste d'ips +indique de mettre à jour la liste d'ips s -> c { "reply" : "banned_ips_refresh" } - - -=== Envoie d'un troll === -c -> s - { - "action" : "put_troll", - "cookie" : "LKJDLAKSJBFLKASN", - "content" : "Un bon troll velu !" + + +=== Envoie d'un troll === +c -> s + { + "action" : "put_troll", + "cookie" : "LKJDLAKSJBFLKASN", + "content" : "Un bon troll velu !" } s -> c @@ -249,7 +249,7 @@ s -> c ou - + === Modification d'un troll === c -> s { @@ -264,7 +264,7 @@ s -> c ou - + === Suppression d'un troll === c -> s { @@ -280,16 +280,16 @@ ou === Envoie message === -Le client envoie un message, le message peut répondre à un certain nombre d'autres messages. -"answer_to" n'est pas obligatoire. +Le client envoie un message, le message peut répondre à un certain nombre d'autres messages. +"answer_to" n'est pas obligatoire. -c -> s - { - "action" : "put_message", - "cookie" : "LKJDLAKSJBFLKASN", - "nick" : "Paul", - "content" : "Bonjour", - "answer_to" : [ 345, 532, ... ] +c -> s + { + "action" : "put_message", + "cookie" : "LKJDLAKSJBFLKASN", + "nick" : "Paul", + "content" : "Bonjour", + "answer_to" : [ 345, 532, ... ] } s -> c @@ -354,7 +354,7 @@ s -> c } -=== Débannissement === +=== Débannissement === c -> s { "action" : "unban", @@ -366,23 +366,23 @@ s -> c ou - - -=== Ajout d'une correction d'un messages === -Le client envoie un correctif sous la forme de texte supplémentaire à appondre au dernier messages. -Le message est appondu avec un " +++ " devant, par exemple : -> Gnome c'est mieux que KDE +++ Euh non ok, c'est faux - -c -> s - { - "action" : "correction", - "cookie" : "LKJDLAKSJBFLKASN", - "content" : "Euh non ok, c'est faux" - } - -s -> c - { - "reply" : "correction", - "status" : "ok" | "error", - "message_error" : "blabla" + + +=== Ajout d'une correction d'un messages === +Le client envoie un correctif sous la forme de texte supplémentaire à appondre au dernier messages. +Le message est appondu avec un " +++ " devant, par exemple : +> Gnome c'est mieux que KDE +++ Euh non ok, c'est faux + +c -> s + { + "action" : "correction", + "cookie" : "LKJDLAKSJBFLKASN", + "content" : "Euh non ok, c'est faux" + } + +s -> c + { + "reply" : "correction", + "status" : "ok" | "error", + "message_error" : "blabla" } diff --git a/doc/technique.txt b/doc/technique.txt index 74a6360..458c81f 100644 --- a/doc/technique.txt +++ b/doc/technique.txt @@ -3,8 +3,8 @@ Euphorik - doc technique == euphorik.js == Sequences : - * Chargement d'une page - + * Chargement d'une page + === Client === == pageMinichat.js == === Classes === @@ -19,10 +19,10 @@ Mesure du temps d'execution pour : avec +native : 3.41 Conclusion : - l'ajout de +native n'a pas de répercussions significatives sur les performances, cela provient - surement du fait que le gros du travail est fait du coté de la base de donnée Mnesia. + l'ajout de +native n'a pas de répercussions significatives sur les performances, cela provient + surement du fait que le gros du travail est fait du coté de la base de donnée Mnesia. -=== Séquences === +=== Séquences === * Attente de nouveaux messages a) Messages.rafraichirMessages b) pour chaque conversation @@ -30,18 +30,18 @@ Conclusion : ii) Conversation.flush * Ajout d'un message - PageMinichat.envoyerMessage(pseudo, message) : requête AJAX + PageMinichat.envoyerMessage(pseudo, message) : requête AJAX * Extraction d'une conversation a) Conversation.click b) Client.ajouterConversation(idMess) - c) Client.flush(false) // mise à jour du profile de manière synchrone + c) Client.flush(false) // mise à jour du profile de manière synchrone d) Messages.rafraichirMessages(true) * Suppression d'une conversation === Exemple de conversation === -Utilisé lors des tests +Utilisé lors des tests m1 m2 -> m1 diff --git a/js/pageMinichat.js b/js/pageMinichat.js index b09b4ca..7ad6c5f 100755 --- a/js/pageMinichat.js +++ b/js/pageMinichat.js @@ -167,7 +167,7 @@ PageMinichat.prototype.decharger = function() PageMinichat.prototype.getJSONMessage = function(pseudo, message) { - repondA = [] + var repondA = [] for (var id in this.messages.messagesRepond) repondA.push(parseInt(id)) // FIXME : une propriété ne peut pas être de type int ? @@ -700,7 +700,7 @@ Conversation.prototype.flush = function(funClickOuvrirConv, funClickMessage) { $("#outilsBan", this).hide() } - ) + ) } ) DOM.prependTo("#conversations #" + this.getId()) @@ -724,7 +724,7 @@ Conversation.prototype.afficherConversation = function(id) var message = this.messagesParId[id] if (message == undefined) return - mess = message.getConversation(this) + var mess = message.getConversation(this) // FIXME : cet appel est très lent $("#conversations #" + this.getId() + " .message").each( @@ -738,14 +738,14 @@ Conversation.prototype.afficherConversation = function(id) { jq.removeClass("cache") switch (statut) - { - // "repondu" et "reponse" sont prioritaitres à "proprietaire" - // contrairement à la vue normale (sans mise en évidence d'une conversation) - case 3 : - jq.addClass("repondu") - break; - case 2 : - jq.addClass("reponse") + { + // "repondu" et "reponse" sont prioritaitres à "proprietaire" + // contrairement à la vue normale (sans mise en évidence d'une conversation) + case 3 : + jq.addClass("repondu") + break; + case 2 : + jq.addClass("reponse") break; case 1 : jq.addClass("proprietaire") @@ -789,7 +789,8 @@ function Messages(client, formateur, util) } /** - * Si le message + * Permet de définir un message comme étant ou n'étant plus un message auquel l'utilisateur + * répond. */ Messages.prototype.toggleMessageRepond = function(mess) { diff --git a/modules/erl/euphorik_bd.erl b/modules/erl/euphorik_bd.erl index 4c27577..a0ffc3a 100755 --- a/modules/erl/euphorik_bd.erl +++ b/modules/erl/euphorik_bd.erl @@ -15,349 +15,349 @@ % % You should have received a copy of the GNU General Public License % along with Euphorik. If not, see . -% -% Ce module permet de gérer les données persistantes lié au site d'euphorik.ch. -% Il permet d'ajouter des message, de demande les messages sur une page donnée, etc.. -% Ce module utilise une base mnesia. +% +% Ce module permet de gérer les données persistantes lié au site d'euphorik.ch. +% Il permet d'ajouter des message, de demande les messages sur une page donnée, etc.. +% Ce module utilise une base mnesia. % @author G.Burri - - + + -module(euphorik_bd). --export([ - % gestion : - create/0, - connect/0, - connect/1, - reset/0, - - % users : - nouveau_user/2, - nouveau_user/3, - set_profile/10, - update_date_derniere_connexion/1, - update_ip/2, - update_pseudo_user/2, - print_users/0, - print_users/1, - print_user/1, - user_by_cookie/1, - user_by_id/1, - user_by_login/1, - user_by_login_password/2, - user_by_mess/1, - toggle_ek_master/1, - css_from_user_cookie/1, - - % messages :e - nouveau_message/3, - nouveau_message_sys/1, - nouveau_message_sys/2, - messages/1, +-export([ + % gestion : + create/0, + connect/0, + connect/1, + reset/0, + + % users : + nouveau_user/2, + nouveau_user/3, + set_profile/10, + update_date_derniere_connexion/1, + update_ip/2, + update_pseudo_user/2, + print_users/0, + print_users/1, + print_user/1, + user_by_cookie/1, + user_by_id/1, + user_by_login/1, + user_by_login_password/2, + user_by_mess/1, + toggle_ek_master/1, + css_from_user_cookie/1, + + % messages :e + nouveau_message/3, + nouveau_message_sys/1, + nouveau_message_sys/2, + messages/1, messages/2, - messages/3, - message_by_id/1, - messages_by_ids/1, - message_existe/1, - reponses/0, - parents/1, - enfants/1, - est_une_reponse_a_user/2, - a_repondu_a_message/2, - possede_message/2, - - % ip : - list_ban/0, - ban/2, - deban/1, - est_banni/1, - can_register/1, - - % trolls : - trolls/0, - trolls/1, - put_troll/2, - mod_troll/2, - del_troll/1, - troll_by_id/1, - current_troll/0, - elire_troll/0, - message_id_associe/1, - - % utiles : + messages/3, + message_by_id/1, + messages_by_ids/1, + message_existe/1, + reponses/0, + parents/1, + enfants/1, + est_une_reponse_a_user/2, + a_repondu_a_message/2, + possede_message/2, + + % ip : + list_ban/0, + ban/2, + deban/1, + est_banni/1, + can_register/1, + + % trolls : + trolls/0, + trolls/1, + put_troll/2, + mod_troll/2, + del_troll/1, + troll_by_id/1, + current_troll/0, + elire_troll/0, + message_id_associe/1, + + % utiles : resultat_transaction/1 -]). +]). -import(qlc, [e/2, q/1, cursor/2]). --include("../include/euphorik_bd.hrl"). +-include("../include/euphorik_bd.hrl"). -include("../include/euphorik_defines.hrl"). -include_lib("stdlib/include/qlc.hrl"). - - -% Instructions pour créer une nouvelle base : -% $erl -sname yaws -mnesia dir '"/projets/euphorik/BD"' -% voir doc/installation.txt -% >l(euphorik_bd). -% >euphorik_bd:create(). -create() -> - mnesia:stop(), - mnesia:delete_schema([node()]), - mnesia:create_schema([node()]), % nécessaire pour les tables sur disc - mnesia:start(), - create_tables(), - reset(). - -create_tables() -> - mnesia:create_table(counter, [ - {attributes, record_info(fields, counter)}, - {disc_copies, [node()]} - ]), - mnesia:create_table(proprietes, [ - {attributes, record_info(fields, proprietes)}, - {disc_copies, [node()]} - ]), - mnesia:create_table(minichat, [ - {attributes, record_info(fields, minichat)}, - {index, [auteur_id, troll_id]}, - {disc_copies, [node()]} - ]), - mnesia:create_table(reponse_minichat, [ - {type, bag}, - {attributes, record_info(fields, reponse_minichat)}, - {index, [cible]}, - {disc_copies, [node()]} - ]), - mnesia:create_table(user, [ - {attributes, record_info(fields, user)}, - {index, [cookie, login]}, - {disc_copies, [node()]} - ]), - mnesia:create_table(ip_table, [ - {attributes, record_info(fields, ip_table)}, - {disc_copies, [node()]} - ]), - mnesia:create_table(troll, [ - {attributes, record_info(fields, troll)}, - {index, [date_post]}, - {disc_copies, [node()]} - ]). - - -% Connexion à la base de données de yaws sur overnux -connect() -> - connect(yaws@flynux). -connect(Node) -> - mnesia:start(), - mnesia:change_config(extra_db_nodes, [Node]). - - + + +% Instructions pour créer une nouvelle base : +% $erl -sname yaws -mnesia dir '"/projets/euphorik/BD"' +% voir doc/installation.txt +% >l(euphorik_bd). +% >euphorik_bd:create(). +create() -> + mnesia:stop(), + mnesia:delete_schema([node()]), + mnesia:create_schema([node()]), % nécessaire pour les tables sur disc + mnesia:start(), + create_tables(), + reset(). + +create_tables() -> + mnesia:create_table(counter, [ + {attributes, record_info(fields, counter)}, + {disc_copies, [node()]} + ]), + mnesia:create_table(proprietes, [ + {attributes, record_info(fields, proprietes)}, + {disc_copies, [node()]} + ]), + mnesia:create_table(minichat, [ + {attributes, record_info(fields, minichat)}, + {index, [auteur_id, troll_id]}, + {disc_copies, [node()]} + ]), + mnesia:create_table(reponse_minichat, [ + {type, bag}, + {attributes, record_info(fields, reponse_minichat)}, + {index, [cible]}, + {disc_copies, [node()]} + ]), + mnesia:create_table(user, [ + {attributes, record_info(fields, user)}, + {index, [cookie, login]}, + {disc_copies, [node()]} + ]), + mnesia:create_table(ip_table, [ + {attributes, record_info(fields, ip_table)}, + {disc_copies, [node()]} + ]), + mnesia:create_table(troll, [ + {attributes, record_info(fields, troll)}, + {index, [date_post]}, + {disc_copies, [node()]} + ]). + + +% Connexion à la base de données de yaws sur overnux +connect() -> + connect(yaws@flynux). +connect(Node) -> + mnesia:start(), + mnesia:change_config(extra_db_nodes, [Node]). + + % Efface tous les users, minichat_reponse et minichat. reset() -> - mnesia:clear_table(counter), + mnesia:clear_table(counter), mnesia:clear_table(proprietes), mnesia:clear_table(user), mnesia:clear_table(reponse_minichat), - mnesia:clear_table(minichat), - mnesia:clear_table(troll), + mnesia:clear_table(minichat), + mnesia:clear_table(troll), mnesia:clear_table(ip_table), % crée l'utilisateur root - mnesia:transaction(fun() -> + mnesia:transaction(fun() -> mnesia:write(#proprietes{nom = version, valeur = ?VERSION_BD}), User = #user{id = 0, pseudo = "Sys", login = "Sys", date_creation = now(), date_derniere_connexion = now(), ek_master = true}, mnesia:write(User), User - end). - - -% Ajoute un nouveau user et le renvoie -nouveau_user(Pseudo, Cookie) -> - F = fun() -> - Id = nouvel_id(user), - User = #user{id = Id, cookie = Cookie, pseudo = Pseudo, date_creation = now(), date_derniere_connexion = now()}, - mnesia:write(User), - User - end, - resultat_transaction(mnesia:transaction(F)). - - -% Ajoute un nouveau user et le renvoie -nouveau_user(Login, Password, Cookie) -> - F = fun() -> - Id = nouvel_id(user), - User = #user{id = Id, cookie = Cookie, pseudo = Login, login = Login, password = Password, date_creation = now(), date_derniere_connexion = now()}, - mnesia:write(User), - User - end, - resultat_transaction(mnesia:transaction(F)). - - -% Mise à par Cookie les autres peuvent être undefined ce qui veut dire qu'ils ne seront pas modifié. -set_profile(Cookie, Login, Password, Pseudo, Email, Css, Nick_format, View_times, View_tooltips, Conversations) -> - if Nick_format =:= nick; Nick_format =:= login; Nick_format =:= nick_login -> - resultat_transaction(mnesia:transaction( - fun() -> - case user_by_cookie(Cookie) of - {ok, User} -> - case user_by_login(Login) of - {ok, U} when Login =/= [], U#user.id =/= User#user.id -> - login_deja_pris; - _ -> - User_modifie = User#user{ - % TODO : pourquoi ne pas tester avec la valeur "undefined" plutôt qu'avec "is_list" ? - % TODO : validation plus strict des données (pas de page négative dans les conv par exemple) - login = if is_list(Login) -> Login; true -> User#user.login end, - password = if is_list(Password) andalso Password =/= [] -> Password; true -> User#user.password end, - pseudo = if is_list(Pseudo) -> Pseudo; true -> User#user.pseudo end, - email = if is_list(Email) -> Email; true -> User#user.email end, - css = if is_list(Css) -> Css; true -> User#user.css end, - nick_format = Nick_format, - view_times = View_times, - view_tooltips = View_tooltips, - conversations = if is_list(Conversations) -> Conversations; true -> User#user.conversations end - }, - mnesia:write(User_modifie), - ok - end; - _ -> erreur - end - end - )); - true -> - erreur - end. - - -% Met à jour la date de la dernière connexion d'un utilisateur à maintenant -update_date_derniere_connexion(User_id) -> - mnesia:transaction( - fun() -> - case mnesia:wread({user, User_id}) of - [User] -> - mnesia:write(User#user{date_derniere_connexion = now()}); - _ -> - mnesia:abort("update_date_derniere_connexion: User inconnu") - end - end - ). - - -% Met à jour l'ip d'un user -update_ip(User_id, IP) -> - mnesia:transaction( - fun() -> - case mnesia:wread({user, User_id}) of - [User] -> - mnesia:write(User#user{last_ip = IP}); - _ -> - mnesia:abort("update_ip: User inconnu") - end - end - ). - - -% Met à jour le pseudo du user -update_pseudo_user(UserId, Pseudo) -> - mnesia:transaction( - fun() -> - case mnesia:wread({user, UserId}) of - [User] when User#user.pseudo =/= Pseudo -> - mnesia:write(User#user{pseudo = Pseudo}); - _ -> - mnesia:abort("update_pseudo_user: User inconnu ou pseudo deja à jour") - end - end - ). - - -% Affiche N user trié par leur date de dernière connexion. -% Opt est une liste d'option d'affichage : -% * ekmaster : n'affiche que les admins -print_users(N, Opt) -> - AfficheQueLesEkMaster = lists:any(fun(O) -> O =:= ekmaster end, Opt), - resultat_transaction(mnesia:transaction(fun() -> - C = cursor( - qlc:keysort( - #user.date_derniere_connexion, - if AfficheQueLesEkMaster -> - q([E || E <- mnesia:table(user), E#user.ek_master =:= true]); - true -> - q([E || E <- mnesia:table(user)]) - end, - [{order, descending}] - ), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ), - Users = qlc:next_answers(C, N), - lists:foreach( - fun(U) -> - print_user(U) - end, - Users - ), - qlc:delete_cursor(C) - end)). - - -% Affiche tous les users. -print_users(Opt) -> - print_users(all_remaining, Opt). - -% Affiche tous les users. -print_users() -> - print_users(all_remaining, []). - -print_user(User) when is_record(User, user) -> - #user{id = Id, pseudo = Pseudo, login = Login, ek_master = Ek_master, date_derniere_connexion = Date, last_ip = IP} = User, - {{Annee, Mois, Jour}, {Heure, Min, _}} = calendar:now_to_local_time(Date), - io:format( - % id pseudo (login) IP Jour Mois Année Heure Minute - "~4w : ~10.10..s(~10.10..s) ~s ~2w.~2.2.0w.~w - ~2wh~2.2.0w~n", - [ - Id, - if Ek_master -> "*"; true -> "" end ++ Pseudo, - Login, - euphorik_common:serialize_ip(IP), - Jour, Mois, Annee, Heure, Min - ] - ); -% Affichage d'un user en fonction de son login -print_user(Login) when is_list(Login) -> - case user_by_login(Login) of - {ok, User} -> - print_user(User); - _ -> - {erreur, "Login pas trouvé : " ++ Login} - end; -% Affichage d'un user en fonction de son id -print_user(Id) when is_integer(Id) -> - case user_by_id(Id) of - {ok, User} -> - print_user(User); - _ -> - {erreur, "Id pas trouvé : " ++ integer_to_list(Id)} - end. - - -% Est-ce qu'un utilisateur existe en fonction de son cookie ? -% Renvoie {ok, User} ou erreur -user_by_cookie(Cookie) -> - resultat_transaction(mnesia:transaction( - fun() -> - case e(q([E || E <- mnesia:table(user), E#user.cookie =:= Cookie]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [User] -> {ok, User}; - _ -> erreur - end - end - )). - - -user_by_id(ID) -> - resultat_transaction(mnesia:transaction( - fun() -> - case e(q([E || E <- mnesia:table(user), E#user.id =:= ID]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [User] -> {ok, User}; - _ -> erreur - end - end - )). + end). + + +% Ajoute un nouveau user et le renvoie +nouveau_user(Pseudo, Cookie) -> + F = fun() -> + Id = nouvel_id(user), + User = #user{id = Id, cookie = Cookie, pseudo = Pseudo, date_creation = now(), date_derniere_connexion = now()}, + mnesia:write(User), + User + end, + resultat_transaction(mnesia:transaction(F)). + + +% Ajoute un nouveau user et le renvoie +nouveau_user(Login, Password, Cookie) -> + F = fun() -> + Id = nouvel_id(user), + User = #user{id = Id, cookie = Cookie, pseudo = Login, login = Login, password = Password, date_creation = now(), date_derniere_connexion = now()}, + mnesia:write(User), + User + end, + resultat_transaction(mnesia:transaction(F)). + + +% Mise à par Cookie les autres peuvent être undefined ce qui veut dire qu'ils ne seront pas modifié. +set_profile(Cookie, Login, Password, Pseudo, Email, Css, Nick_format, View_times, View_tooltips, Conversations) -> + if Nick_format =:= nick; Nick_format =:= login; Nick_format =:= nick_login -> + resultat_transaction(mnesia:transaction( + fun() -> + case user_by_cookie(Cookie) of + {ok, User} -> + case user_by_login(Login) of + {ok, U} when Login =/= [], U#user.id =/= User#user.id -> + login_deja_pris; + _ -> + User_modifie = User#user{ + % TODO : pourquoi ne pas tester avec la valeur "undefined" plutôt qu'avec "is_list" ? + % TODO : validation plus strict des données (pas de page négative dans les conv par exemple) + login = if is_list(Login) -> Login; true -> User#user.login end, + password = if is_list(Password) andalso Password =/= [] -> Password; true -> User#user.password end, + pseudo = if is_list(Pseudo) -> Pseudo; true -> User#user.pseudo end, + email = if is_list(Email) -> Email; true -> User#user.email end, + css = if is_list(Css) -> Css; true -> User#user.css end, + nick_format = Nick_format, + view_times = View_times, + view_tooltips = View_tooltips, + conversations = if is_list(Conversations) -> Conversations; true -> User#user.conversations end + }, + mnesia:write(User_modifie), + ok + end; + _ -> erreur + end + end + )); + true -> + erreur + end. + + +% Met à jour la date de la dernière connexion d'un utilisateur à maintenant +update_date_derniere_connexion(User_id) -> + mnesia:transaction( + fun() -> + case mnesia:wread({user, User_id}) of + [User] -> + mnesia:write(User#user{date_derniere_connexion = now()}); + _ -> + mnesia:abort("update_date_derniere_connexion: User inconnu") + end + end + ). + + +% Met à jour l'ip d'un user +update_ip(User_id, IP) -> + mnesia:transaction( + fun() -> + case mnesia:wread({user, User_id}) of + [User] -> + mnesia:write(User#user{last_ip = IP}); + _ -> + mnesia:abort("update_ip: User inconnu") + end + end + ). + + +% Met à jour le pseudo du user +update_pseudo_user(UserId, Pseudo) -> + mnesia:transaction( + fun() -> + case mnesia:wread({user, UserId}) of + [User] when User#user.pseudo =/= Pseudo -> + mnesia:write(User#user{pseudo = Pseudo}); + _ -> + mnesia:abort("update_pseudo_user: User inconnu ou pseudo deja à jour") + end + end + ). + + +% Affiche N user trié par leur date de dernière connexion. +% Opt est une liste d'option d'affichage : +% * ekmaster : n'affiche que les admins +print_users(N, Opt) -> + AfficheQueLesEkMaster = lists:any(fun(O) -> O =:= ekmaster end, Opt), + resultat_transaction(mnesia:transaction(fun() -> + C = cursor( + qlc:keysort( + #user.date_derniere_connexion, + if AfficheQueLesEkMaster -> + q([E || E <- mnesia:table(user), E#user.ek_master =:= true]); + true -> + q([E || E <- mnesia:table(user)]) + end, + [{order, descending}] + ), + [{tmpdir, ?KEY_SORT_TEMP_DIR}] + ), + Users = qlc:next_answers(C, N), + lists:foreach( + fun(U) -> + print_user(U) + end, + Users + ), + qlc:delete_cursor(C) + end)). + + +% Affiche tous les users. +print_users(Opt) -> + print_users(all_remaining, Opt). + +% Affiche tous les users. +print_users() -> + print_users(all_remaining, []). + +print_user(User) when is_record(User, user) -> + #user{id = Id, pseudo = Pseudo, login = Login, ek_master = Ek_master, date_derniere_connexion = Date, last_ip = IP} = User, + {{Annee, Mois, Jour}, {Heure, Min, _}} = calendar:now_to_local_time(Date), + io:format( + % id pseudo (login) IP Jour Mois Année Heure Minute + "~4w : ~10.10..s(~10.10..s) ~s ~2w.~2.2.0w.~w - ~2wh~2.2.0w~n", + [ + Id, + if Ek_master -> "*"; true -> "" end ++ Pseudo, + Login, + euphorik_common:serialize_ip(IP), + Jour, Mois, Annee, Heure, Min + ] + ); +% Affichage d'un user en fonction de son login +print_user(Login) when is_list(Login) -> + case user_by_login(Login) of + {ok, User} -> + print_user(User); + _ -> + {erreur, "Login pas trouvé : " ++ Login} + end; +% Affichage d'un user en fonction de son id +print_user(Id) when is_integer(Id) -> + case user_by_id(Id) of + {ok, User} -> + print_user(User); + _ -> + {erreur, "Id pas trouvé : " ++ integer_to_list(Id)} + end. + + +% Est-ce qu'un utilisateur existe en fonction de son cookie ? +% Renvoie {ok, User} ou erreur +user_by_cookie(Cookie) -> + resultat_transaction(mnesia:transaction( + fun() -> + case e(q([E || E <- mnesia:table(user), E#user.cookie =:= Cookie]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [User] -> {ok, User}; + _ -> erreur + end + end + )). + + +user_by_id(ID) -> + resultat_transaction(mnesia:transaction( + fun() -> + case e(q([E || E <- mnesia:table(user), E#user.id =:= ID]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [User] -> {ok, User}; + _ -> erreur + end + end + )). user_by_login(Login) -> @@ -370,552 +370,552 @@ user_by_login(Login) -> end end )). - - -toggle_ek_master(User_id) -> - resultat_transaction(mnesia:transaction( - fun() -> - Users = e(q([E || E <- mnesia:table(user), E#user.id =:= User_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]), - case Users of - [User] -> - mnesia:write(User#user{ek_master = not User#user.ek_master}); - _ -> erreur - end - end - )). - - -% Renvoie une chaine représentant le cookie ou undefined si pas trouvé. -css_from_user_cookie(Cookie) -> - case user_by_cookie(Cookie) of - {ok, User} -> - User#user.css; - _ -> - undefined - end. - - -user_by_login_password(Login, Password) -> - resultat_transaction(mnesia:transaction( - fun() -> - case e(q([E || E <- mnesia:table(user), E#user.login =:= Login, E#user.password =:= Password]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [User | _] -> {ok, User}; - _ -> erreur - end - end - )). - - -% Renvoie {ok, User} où User est un #user possédant le message donné. -user_by_mess(Id) -> - resultat_transaction(mnesia:transaction( - fun() -> - case e(q([U || U <- mnesia:table(user), M <- mnesia:table(minichat), M#minichat.id =:= Id, M#minichat.auteur_id =:= U#user.id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [User | _] -> {ok, User}; - _ -> erreur - end - end + + +toggle_ek_master(User_id) -> + resultat_transaction(mnesia:transaction( + fun() -> + Users = e(q([E || E <- mnesia:table(user), E#user.id =:= User_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]), + case Users of + [User] -> + mnesia:write(User#user{ek_master = not User#user.ek_master}); + _ -> erreur + end + end + )). + + +% Renvoie une chaine représentant le cookie ou undefined si pas trouvé. +css_from_user_cookie(Cookie) -> + case user_by_cookie(Cookie) of + {ok, User} -> + User#user.css; + _ -> + undefined + end. + + +user_by_login_password(Login, Password) -> + resultat_transaction(mnesia:transaction( + fun() -> + case e(q([E || E <- mnesia:table(user), E#user.login =:= Login, E#user.password =:= Password]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [User | _] -> {ok, User}; + _ -> erreur + end + end + )). + + +% Renvoie {ok, User} où User est un #user possédant le message donné. +user_by_mess(Id) -> + resultat_transaction(mnesia:transaction( + fun() -> + case e(q([U || U <- mnesia:table(user), M <- mnesia:table(minichat), M#minichat.id =:= Id, M#minichat.auteur_id =:= U#user.id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [User | _] -> {ok, User}; + _ -> erreur + end + end + )). + + +% Ajoute un message. Repond_A est une liste d'id auquel le message répond +% retourne soit l'id du message soit {erreur, }. +nouveau_message(Mess, Auteur_id, Repond_A_ids) -> + % regarde si les id 'Repond_A' existent + F = fun() -> + Repond_A = e( + q([M || M <- mnesia:table(minichat), lists:member(M#minichat.id, Repond_A_ids)]), + [{tmpdir, ?KEY_SORT_TEMP_DIR}] + ), + Racine_id = case Repond_A of + [] -> undefined; + [M | _ ] -> M#minichat.racine_id + end, + % est-ce que l'auteur existe ? + case e(q([E || E <- mnesia:table(user), E#user.id =:= Auteur_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [Auteur] -> + if length(Repond_A) =/= length(Repond_A_ids) -> + {erreur, "Un ou plusieurs messages introuvable"}; + true -> + % comparaison entre la date du dernier poste et maintenant (gestion du flood) + Delta = delta_date_ms(Auteur#user.date_derniere_connexion, now()), + Nouvel_indice_flood = Auteur#user.indice_flood + if Delta =< ?DUREE_SPAM -> 2; true -> -1 end, + Auteur_maj = Auteur#user{ + indice_flood = if Nouvel_indice_flood > ?INDICE_SPAM_MAX -> ?INDICE_SPAM_MAX; Nouvel_indice_flood < 0 -> 0; true -> Nouvel_indice_flood end, + date_derniere_connexion = now() + }, + % est-ce que l'auteur à trop floodé ? + if Auteur#user.indice_flood =/= ?INDICE_SPAM_MAX, Auteur_maj#user.indice_flood =:= ?INDICE_SPAM_MAX, Delta =< ?DUREE_BLOCAGE_SPAM -> + mnesia:write(Auteur#user{indice_flood = Auteur_maj#user.indice_flood}), + nouveau_message_sys("''" ++ Auteur#user.pseudo ++ if Auteur#user.login =/= [] -> " (" ++ Auteur#user.login ++ ")"; true -> "" end ++ "'' est bloqué pour " ++ integer_to_list(trunc(?DUREE_BLOCAGE_SPAM / 1000)) ++ " secondes pour cause de flood."); + Auteur#user.indice_flood =:= ?INDICE_SPAM_MAX, Delta =< ?DUREE_BLOCAGE_SPAM -> + {erreur, "Bloqué pour cause de flood"}; + true -> + mnesia:write(Auteur_maj), + Id = nouvel_id(minichat), + inserer_reponses(Id, Repond_A_ids), + mnesia:write(#minichat{ + id = Id, + auteur_id = Auteur#user.id, + date = now(), + pseudo = Auteur#user.pseudo, + contenu = Mess, + racine_id = if Racine_id =:= undefined -> Id; true -> Racine_id end + }), + Id + end + end; + _ -> + {erreur, "L'auteur du message est introuvable"} + end + end, + resultat_transaction(mnesia:transaction(F)). + +% Définit Id_repondant comme étant la réponse à Ids. Ids est une liste d'id. +inserer_reponses(Id_repondant, [Id_mess | Reste]) -> + mnesia:write(#reponse_minichat{repondant = Id_repondant, cible = Id_mess}), + inserer_reponses(Id_repondant, Reste); +inserer_reponses(_, []) -> + ok. + + +% Permet de créer un message système. +% Renvoie l'id du message système +nouveau_message_sys(Mess) -> + nouveau_message_sys(Mess, undefined). + + +% Création d'un message système lié à un troll. +nouveau_message_sys(Mess, Troll_id) -> + {ok, Root} = user_by_id(0), + resultat_transaction(mnesia:transaction( + fun() -> + Id = nouvel_id(minichat), + mnesia:write(#minichat{id=Id, auteur_id=0, date=now(), pseudo=Root#user.pseudo, contenu=Mess, troll_id=Troll_id, racine_id=Id}), + Id + end + )). + + +% Renvoie N messages se trouvant sur la première page +messages(N) -> + messages(N, 1). + + +% Renvoie N messages se trouvant sur la page P +messages(N, P) -> + F = fun() -> + C = cursor( + qlc:keysort( + #minichat.id, + q([E#minichat{contenu = contenu_message(E)} || E <- mnesia:table(minichat)]), + [{order, descending}] + ), + [{tmpdir, ?KEY_SORT_TEMP_DIR}] + ), + if P > 1 -> qlc:next_answers(C, N * (P - 1)); + true -> ok + end, + R = qlc:next_answers(C, N), + qlc:delete_cursor(C), + R + end, + lists:reverse(resultat_transaction(mnesia:transaction(F))). + + +% Renvoie les messages manquants pour la page P en sachant qu'il y a N message +% par page et que le dernier message que l'on possède est Id +messages(Id, N, P) -> + lists:filter(fun (M) -> M#minichat.id > Id end, messages(N, P)). + + +% Renvoie {ok, #minichat} (voir #minichat de euphorik_bd.hrl) à partir de son id. +message_by_id(Id) -> + resultat_transaction(mnesia:transaction( + fun() -> + case mnesia:read({minichat, Id}) of + [] -> erreur; + [M] -> + {ok, M#minichat{contenu = contenu_message(M)}} + end + end + )). + + +% Renvoie le contenu d'un message donnée, à utiliser à l'intérieur d'une transaction. +% TODO : Cette fonction pourrait être remplacé par un "outer-join", est-ce possible avec qlc ? +contenu_message(E) -> + case mnesia:read({troll, E#minichat.troll_id}) of + [] -> E#minichat.contenu; + [T] -> E#minichat.contenu ++ T#troll.content + end. + + +% Renvoie une liste de message (voir #minichat de euphorik_bd.hrl) à partir d'une liste d'id (Ids). +% TODO : optimisations ? serait-ce du O(n) ? +% Bon de toutes façons on s'en fout c'est pas utilisé :) +messages_by_ids(Ids) -> + resultat_transaction(mnesia:transaction( + fun() -> + e(qlc:keysort( + #minichat.id, + q([E || E <- mnesia:table(minichat), lists:any(fun(Id) -> Id =:= E#minichat.id end, Ids)]), + [{order, ascending}] + ),[{tmpdir, ?KEY_SORT_TEMP_DIR}]) + end + )). + + +% Est-ce qu'un message existe ? Renvoie un boolean. +% TODO : ya pas plus simple ? +message_existe(Id) -> + resultat_transaction(mnesia:transaction(fun() -> + length(e(q([E#minichat.id || E <- mnesia:table(minichat), E#minichat.id =:= Id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}])) =:= 1 + end)). + + +% Renvoie les reponses (utilisé normalement uniquement pendant le debug). +reponses() -> + F = fun() -> + e(q([E || E <- mnesia:table(reponse_minichat)]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) + end, + resultat_transaction(mnesia:transaction(F)). + + +% Renvoie les messages auquel M_id répond. +parents(M_id) -> + resultat_transaction(mnesia:transaction( + fun() -> + e(q( + [M || R <- mnesia:table(reponse_minichat), + M <- mnesia:table(minichat), + R#reponse_minichat.repondant =:= M_id, + M#minichat.id =:= R#reponse_minichat.cible]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) + end + )). + + +% Renvoie les message qui repondent à M_id +enfants(M_id) -> + resultat_transaction(mnesia:transaction( + fun() -> + e(q( + [M || R <- mnesia:table(reponse_minichat), + M <- mnesia:table(minichat), + R#reponse_minichat.cible =:= M_id, + M#minichat.id =:= R#reponse_minichat.repondant]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) + end + )). + + +% Est-ce que le message Id_mess est une réponse d'une message de Id_user ? +est_une_reponse_a_user(Id_user, Id_mess) -> + case mnesia:transaction( + fun() -> + e(q([ + M#minichat.auteur_id || M <- mnesia:table(minichat), R <- mnesia:table(reponse_minichat), + M#minichat.auteur_id =:= Id_user, M#minichat.id =:= R#reponse_minichat.cible, R#reponse_minichat.repondant =:= Id_mess + ]), [{unique_all, true}, {tmpdir, ?KEY_SORT_TEMP_DIR}]) + end + ) of + {atomic, [_]} -> true; + _ -> false + end. + + +% Est-ce que Id_user à répondu au message Id_mess +a_repondu_a_message(Id_user, Id_mess) -> + case mnesia:transaction( + fun() -> + e(q([ + M#minichat.auteur_id || M <- mnesia:table(minichat), R <- mnesia:table(reponse_minichat), + R#reponse_minichat.cible =:= Id_mess, R#reponse_minichat.repondant =:= M#minichat.id, M#minichat.auteur_id =:= Id_user + ]), [{unique_all, true}, {tmpdir, ?KEY_SORT_TEMP_DIR}]) + end + ) of + {atomic, [_]} -> true; + _ -> false + end. + + +% Est-ce que Id_user possède Id_mess ? +possede_message(Id_user, Id_mess) -> + case mnesia:transaction( + fun() -> + e(q([E#minichat.auteur_id || E <- mnesia:table(minichat), E#minichat.id =:= Id_mess]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) + end + ) of + {atomic, [Id_user | []]} -> true; + _ -> false + end. + + +% renvoie la liste des ip bannies +% liste de {ip, temps_restant(en minutes), Users} ou Users est une liste de {pseudo, login} +% TODO : déterminer la complexité de cette fonction. (Il n'y a pas d'index sur #user.last_ip) +list_ban() -> + resultat_transaction(mnesia:transaction( + fun() -> + Now = now(), + e(qlc:keysort(1, q([ + { + IP#ip_table.ip, + delta_date_minute(date_plus_minutes(IP#ip_table.ban, IP#ip_table.ban_duration), Now), + e(q([{U#user.pseudo, U#user.login} || U <- mnesia:table(user), U#user.last_ip =:= IP#ip_table.ip]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) + } || + IP <- mnesia:table(ip_table), + if IP#ip_table.ban =:= undefined -> false; true -> date_plus_minutes(IP#ip_table.ban, IP#ip_table.ban_duration) > Now end + ])), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) + end + )). + + +% Bannie une ip pour un certain temps (en minute). +ban(IP, Duration) -> + mnesia:transaction( + fun() -> + case mnesia:wread({ip_table, IP}) of + [IP_tuple] -> + mnesia:write(IP_tuple#ip_table{ban = now(), ban_duration = Duration}); + _ -> + mnesia:write(#ip_table{ip = IP, ban = now(), ban_duration = Duration}) + end + end + ). + + +% Débanni une ip +deban(IP) -> + ban(IP, 0). + + +% Renvoie soit {true, Temps} où Temps est le temps en minutes pendant lequel le user est encore banni +% ou false. +est_banni(User_id) -> + resultat_transaction(mnesia:transaction( + fun() -> + case e(q([ + {IP#ip_table.ban, IP#ip_table.ban_duration} || + U <- mnesia:table(user), + U#user.id =:= User_id, + IP <- mnesia:table(ip_table), + IP#ip_table.ip =:= U#user.last_ip + ]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [{Ban, Ban_duration}] -> + Echeance = date_plus_minutes(Ban, Ban_duration), + Now = now(), + if Echeance < Now -> % l'échéance est passée + false; + true -> + {true, delta_date_minute(Echeance, Now)} + end; + _ -> + false + end + end + )). + + +% Ban est une date tel que retourner par now(). +% Ban_duration est un temps en minutes. +% retourne une date. +date_plus_minutes(Ban, Ban_duration) -> + Duration_sec = Ban_duration * 60, + {MegaSec, Sec, MicroSec} = Ban, + {MegaSec + if Sec + Duration_sec >= 1000000 -> 1; true -> 0 end,(Sec + Duration_sec) rem 1000000, MicroSec}. + + +% Si deux enregistrements consequtifs de la même IP sont fait en moins d'une seconde alors +% ip_table.nb_try_register est incrémenté de 1 sinon il est décrémenté de 1 (jusqu'a 0). +% Si ip_table.nb_try_register vaut 5 alors l'ip ne peux plus s'enregistrer pour une heure. +can_register(IP) -> + resultat_transaction(mnesia:transaction( + fun() -> + case e(q([I || I <- mnesia:table(ip_table), I#ip_table.ip =:= IP]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [] -> + mnesia:write(#ip_table{ip = IP, date_last_try_register = now()}), + true; + [T] -> + Delta = delta_date_ms(T#ip_table.date_last_try_register, now()), + if T#ip_table.nb_try_register =:= ?NB_MAX_FLOOD_REGISTER, Delta < ?TEMPS_BAN_FLOOD_REGISTER -> + false; + true -> + mnesia:write(#ip_table{ + ip = IP, + date_last_try_register = now(), + nb_try_register = T#ip_table.nb_try_register + if Delta < ?TEMPS_FLOOD_REGISTER -> 1; T#ip_table.nb_try_register > 0 -> -1; true -> 0 end + }), + true + end + end + end )). - -% Ajoute un message. Repond_A est une liste d'id auquel le message répond -% retourne soit l'id du message soit {erreur, }. -nouveau_message(Mess, Auteur_id, Repond_A_ids) -> - % regarde si les id 'Repond_A' existent - F = fun() -> - Repond_A = e( - q([M || M <- mnesia:table(minichat), lists:member(M#minichat.id, Repond_A_ids)]), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ), - Racine_id = case Repond_A of - [] -> undefined; - [M | _ ] -> M#minichat.racine_id - end, - % est-ce que l'auteur existe ? - case e(q([E || E <- mnesia:table(user), E#user.id =:= Auteur_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [Auteur] -> - if length(Repond_A) =/= length(Repond_A_ids) -> - {erreur, "Un ou plusieurs messages introuvable"}; - true -> - % comparaison entre la date du dernier poste et maintenant (gestion du flood) - Delta = delta_date_ms(Auteur#user.date_derniere_connexion, now()), - Nouvel_indice_flood = Auteur#user.indice_flood + if Delta =< ?DUREE_SPAM -> 2; true -> -1 end, - Auteur_maj = Auteur#user{ - indice_flood = if Nouvel_indice_flood > ?INDICE_SPAM_MAX -> ?INDICE_SPAM_MAX; Nouvel_indice_flood < 0 -> 0; true -> Nouvel_indice_flood end, - date_derniere_connexion = now() - }, - % est-ce que l'auteur à trop floodé ? - if Auteur#user.indice_flood =/= ?INDICE_SPAM_MAX, Auteur_maj#user.indice_flood =:= ?INDICE_SPAM_MAX, Delta =< ?DUREE_BLOCAGE_SPAM -> - mnesia:write(Auteur#user{indice_flood = Auteur_maj#user.indice_flood}), - nouveau_message_sys("''" ++ Auteur#user.pseudo ++ if Auteur#user.login =/= [] -> " (" ++ Auteur#user.login ++ ")"; true -> "" end ++ "'' est bloqué pour " ++ integer_to_list(trunc(?DUREE_BLOCAGE_SPAM / 1000)) ++ " secondes pour cause de flood."); - Auteur#user.indice_flood =:= ?INDICE_SPAM_MAX, Delta =< ?DUREE_BLOCAGE_SPAM -> - {erreur, "Bloqué pour cause de flood"}; - true -> - mnesia:write(Auteur_maj), - Id = nouvel_id(minichat), - inserer_reponses(Id, Repond_A_ids), - mnesia:write(#minichat{ - id = Id, - auteur_id = Auteur#user.id, - date = now(), - pseudo = Auteur#user.pseudo, - contenu = Mess, - racine_id = if Racine_id =:= undefined -> Id; true -> Racine_id end - }), - Id - end - end; - _ -> - {erreur, "L'auteur du message est introuvable"} - end - end, - resultat_transaction(mnesia:transaction(F)). - -% Définit Id_repondant comme étant la réponse à Ids. Ids est une liste d'id. -inserer_reponses(Id_repondant, [Id_mess | Reste]) -> - mnesia:write(#reponse_minichat{repondant = Id_repondant, cible = Id_mess}), - inserer_reponses(Id_repondant, Reste); -inserer_reponses(_, []) -> - ok. - - -% Permet de créer un message système. -% Renvoie l'id du message système -nouveau_message_sys(Mess) -> - nouveau_message_sys(Mess, undefined). - - -% Création d'un message système lié à un troll. -nouveau_message_sys(Mess, Troll_id) -> - {ok, Root} = user_by_id(0), - resultat_transaction(mnesia:transaction( - fun() -> - Id = nouvel_id(minichat), - mnesia:write(#minichat{id=Id, auteur_id=0, date=now(), pseudo=Root#user.pseudo, contenu=Mess, troll_id=Troll_id, racine_id=Id}), - Id - end - )). - - -% Renvoie N messages se trouvant sur la première page -messages(N) -> - messages(N, 1). - - -% Renvoie N messages se trouvant sur la page P -messages(N, P) -> - F = fun() -> - C = cursor( - qlc:keysort( - #minichat.id, - q([E#minichat{contenu = contenu_message(E)} || E <- mnesia:table(minichat)]), - [{order, descending}] - ), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ), - if P > 1 -> qlc:next_answers(C, N * (P - 1)); - true -> ok - end, - R = qlc:next_answers(C, N), - qlc:delete_cursor(C), - R - end, - lists:reverse(resultat_transaction(mnesia:transaction(F))). - - -% Renvoie les messages manquants pour la page P en sachant qu'il y a N message -% par page et que le dernier message que l'on possède est Id -messages(Id, N, P) -> - lists:filter(fun (M) -> M#minichat.id > Id end, messages(N, P)). - - -% Renvoie {ok, #minichat} (voir #minichat de euphorik_bd.hrl) à partir de son id. -message_by_id(Id) -> - resultat_transaction(mnesia:transaction( - fun() -> - case mnesia:read({minichat, Id}) of - [] -> erreur; - [M] -> - {ok, M#minichat{contenu = contenu_message(M)}} - end - end - )). - - -% Renvoie le contenu d'un message donnée, à utiliser à l'intérieur d'une transaction. -% TODO : Cette fonction pourrait être remplacé par un "outer-join", est-ce possible avec qlc ? -contenu_message(E) -> - case mnesia:read({troll, E#minichat.troll_id}) of - [] -> E#minichat.contenu; - [T] -> E#minichat.contenu ++ T#troll.content - end. - - -% Renvoie une liste de message (voir #minichat de euphorik_bd.hrl) à partir d'une liste d'id (Ids). -% TODO : optimisations ? serait-ce du O(n) ? -% Bon de toutes façons on s'en fout c'est pas utilisé :) -messages_by_ids(Ids) -> - resultat_transaction(mnesia:transaction( - fun() -> - e(qlc:keysort( - #minichat.id, - q([E || E <- mnesia:table(minichat), lists:any(fun(Id) -> Id =:= E#minichat.id end, Ids)]), - [{order, ascending}] - ),[{tmpdir, ?KEY_SORT_TEMP_DIR}]) - end - )). - - -% Est-ce qu'un message existe ? Renvoie un boolean. -% TODO : ya pas plus simple ? -message_existe(Id) -> - resultat_transaction(mnesia:transaction(fun() -> - length(e(q([E#minichat.id || E <- mnesia:table(minichat), E#minichat.id =:= Id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}])) =:= 1 - end)). - - -% Renvoie les reponses (utilisé normalement uniquement pendant le debug). -reponses() -> - F = fun() -> - e(q([E || E <- mnesia:table(reponse_minichat)]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) - end, - resultat_transaction(mnesia:transaction(F)). - - -% Renvoie les messages auquel M_id répond. -parents(M_id) -> - resultat_transaction(mnesia:transaction( - fun() -> - e(q( - [M || R <- mnesia:table(reponse_minichat), - M <- mnesia:table(minichat), - R#reponse_minichat.repondant =:= M_id, - M#minichat.id =:= R#reponse_minichat.cible]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) - end - )). - - -% Renvoie les message qui repondent à M_id -enfants(M_id) -> - resultat_transaction(mnesia:transaction( - fun() -> - e(q( - [M || R <- mnesia:table(reponse_minichat), - M <- mnesia:table(minichat), - R#reponse_minichat.cible =:= M_id, - M#minichat.id =:= R#reponse_minichat.repondant]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) - end - )). - - -% Est-ce que le message Id_mess est une réponse d'une message de Id_user ? -est_une_reponse_a_user(Id_user, Id_mess) -> - case mnesia:transaction( - fun() -> - e(q([ - M#minichat.auteur_id || M <- mnesia:table(minichat), R <- mnesia:table(reponse_minichat), - M#minichat.auteur_id =:= Id_user, M#minichat.id =:= R#reponse_minichat.cible, R#reponse_minichat.repondant =:= Id_mess - ]), [{unique_all, true}, {tmpdir, ?KEY_SORT_TEMP_DIR}]) - end - ) of - {atomic, [_]} -> true; - _ -> false - end. - - -% Est-ce que Id_user à répondu au message Id_mess -a_repondu_a_message(Id_user, Id_mess) -> - case mnesia:transaction( - fun() -> - e(q([ - M#minichat.auteur_id || M <- mnesia:table(minichat), R <- mnesia:table(reponse_minichat), - R#reponse_minichat.cible =:= Id_mess, R#reponse_minichat.repondant =:= M#minichat.id, M#minichat.auteur_id =:= Id_user - ]), [{unique_all, true}, {tmpdir, ?KEY_SORT_TEMP_DIR}]) - end - ) of - {atomic, [_]} -> true; - _ -> false - end. - - -% Est-ce que Id_user possède Id_mess ? -possede_message(Id_user, Id_mess) -> - case mnesia:transaction( - fun() -> - e(q([E#minichat.auteur_id || E <- mnesia:table(minichat), E#minichat.id =:= Id_mess]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) - end - ) of - {atomic, [Id_user | []]} -> true; - _ -> false - end. - - -% renvoie la liste des ip bannies -% liste de {ip, temps_restant(en minutes), Users} ou Users est une liste de {pseudo, login} -% TODO : déterminer la complexité de cette fonction. (Il n'y a pas d'index sur #user.last_ip) -list_ban() -> - resultat_transaction(mnesia:transaction( - fun() -> - Now = now(), - e(qlc:keysort(1, q([ - { - IP#ip_table.ip, - delta_date_minute(date_plus_minutes(IP#ip_table.ban, IP#ip_table.ban_duration), Now), - e(q([{U#user.pseudo, U#user.login} || U <- mnesia:table(user), U#user.last_ip =:= IP#ip_table.ip]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) - } || - IP <- mnesia:table(ip_table), - if IP#ip_table.ban =:= undefined -> false; true -> date_plus_minutes(IP#ip_table.ban, IP#ip_table.ban_duration) > Now end - ])), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) - end - )). - - -% Bannie une ip pour un certain temps (en minute). -ban(IP, Duration) -> - mnesia:transaction( - fun() -> - case mnesia:wread({ip_table, IP}) of - [IP_tuple] -> - mnesia:write(IP_tuple#ip_table{ban = now(), ban_duration = Duration}); - _ -> - mnesia:write(#ip_table{ip = IP, ban = now(), ban_duration = Duration}) - end - end - ). - - -% Débanni une ip -deban(IP) -> - ban(IP, 0). - - -% Renvoie soit {true, Temps} où Temps est le temps en minutes pendant lequel le user est encore banni -% ou false. -est_banni(User_id) -> - resultat_transaction(mnesia:transaction( - fun() -> - case e(q([ - {IP#ip_table.ban, IP#ip_table.ban_duration} || - U <- mnesia:table(user), - U#user.id =:= User_id, - IP <- mnesia:table(ip_table), - IP#ip_table.ip =:= U#user.last_ip - ]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [{Ban, Ban_duration}] -> - Echeance = date_plus_minutes(Ban, Ban_duration), - Now = now(), - if Echeance < Now -> % l'échéance est passée - false; - true -> - {true, delta_date_minute(Echeance, Now)} - end; - _ -> - false - end - end - )). - - -% Ban est une date tel que retourner par now(). -% Ban_duration est un temps en minutes. -% retourne une date. -date_plus_minutes(Ban, Ban_duration) -> - Duration_sec = Ban_duration * 60, - {MegaSec, Sec, MicroSec} = Ban, - {MegaSec + if Sec + Duration_sec >= 1000000 -> 1; true -> 0 end,(Sec + Duration_sec) rem 1000000, MicroSec}. - - -% Si deux enregistrements consequtifs de la même IP sont fait en moins d'une seconde alors -% ip_table.nb_try_register est incrémenté de 1 sinon il est décrémenté de 1 (jusqu'a 0). -% Si ip_table.nb_try_register vaut 5 alors l'ip ne peux plus s'enregistrer pour une heure. -can_register(IP) -> - resultat_transaction(mnesia:transaction( - fun() -> - case e(q([I || I <- mnesia:table(ip_table), I#ip_table.ip =:= IP]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [] -> - mnesia:write(#ip_table{ip = IP, date_last_try_register = now()}), - true; - [T] -> - Delta = delta_date_ms(T#ip_table.date_last_try_register, now()), - if T#ip_table.nb_try_register =:= ?NB_MAX_FLOOD_REGISTER, Delta < ?TEMPS_BAN_FLOOD_REGISTER -> - false; - true -> - mnesia:write(#ip_table{ - ip = IP, - date_last_try_register = now(), - nb_try_register = T#ip_table.nb_try_register + if Delta < ?TEMPS_FLOOD_REGISTER -> 1; T#ip_table.nb_try_register > 0 -> -1; true -> 0 end - }), - true - end - end - end - )). - - -% Renvoie tous les trolls -trolls() -> - resultat_transaction(mnesia:transaction( - fun() -> - e(qlc:keysort(#troll.id, q([T || T <- mnesia:table(troll)])), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) - end - )). - - -% Renvoie les trolls manquants posté après Last_id. -trolls(Last_id) -> - resultat_transaction(mnesia:transaction( - fun() -> - e(qlc:keysort(#troll.id, q([T || T <- mnesia:table(troll), T#troll.id > Last_id, T#troll.date_post =:= undefined])), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) - end - )). - - - % Crée un nouveau troll. - % Renvoie l'id du nouveau troll - % ou max_troll_reached_per_user si le nombre de troll posté par l'utilisateur max a été atteind - % ou max_troll_reached si le nombre de troll posté max a été atteind - % ou user_unknown -put_troll(User_id, Content) -> - resultat_transaction(mnesia:transaction( - fun() -> - % control le nombre de troll déjà posté - Nb_troll_poste_par_user = length(e(q( - [ - E#troll.id || E <- mnesia:table(troll), - E#troll.id_user =:= User_id, - E#troll.date_post =:= undefined - ] - ), [{tmpdir, ?KEY_SORT_TEMP_DIR}])), - Nb_troll_poste_total = length(e(q( - [ - E#troll.id || E <- mnesia:table(troll), - E#troll.date_post =:= undefined - ] - ), [{tmpdir, ?KEY_SORT_TEMP_DIR}])), - User = user_by_id(User_id), - case User of - {ok, _} -> - if Nb_troll_poste_par_user >= ?NB_MAX_TROLL_WAITING_BY_USER -> - max_troll_reached_per_user; - Nb_troll_poste_total >= ?NB_MAX_TROLL_WAITING -> - max_troll_reached; - true -> - Id = nouvel_id(minichat), - mnesia:write(#troll{id = Id, id_user = User_id, date_create = now(), content = Content}), - Id - end; - _ -> - user_unknown - end - end - )). - - -% renvoie ok | erreur -mod_troll(Troll_id, Content) -> - mnesia:transaction( - fun() -> - case mnesia:wread({troll, Troll_id}) of - [Troll = #troll{date_post = undefined}] -> - mnesia:write(Troll#troll{content = Content}); - _ -> - mnesia:abort("mod_troll: Troll inconnu ou déjà posté") - end - end - ). - - -del_troll(Troll_id) -> - mnesia:transaction( - fun() -> - case mnesia:wread({troll, Troll_id}) of - [#troll{date_post = undefined}] -> - mnesia:delete({troll, Troll_id}); - _ -> - mnesia:abort("mod_troll: Troll inconnu ou déjà posté") - end - end - ). - - -troll_by_id(Troll_id) -> - resultat_transaction(mnesia:transaction( - fun() -> - case e(q([T || T <- mnesia:table(troll), T#troll.id =:= Troll_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [T] -> {ok, T}; - _ -> - erreur - end - end - )). - - -% Renvoie le troll actuel qui se trouve sur la page principale. -% Renvois aucun si pas de troll courant. -current_troll() -> - resultat_transaction(mnesia:transaction( - fun() -> - C = cursor(qlc:keysort(#troll.date_post, q([T || T <- mnesia:table(troll), T#troll.date_post =/= undefined]), [{order, descending}]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]), - R = case qlc:next_answers(C, 1) of - [T] -> T; - _ -> aucun - end, - qlc:delete_cursor(C), - R - end - )). - - -% Elit un troll au hasard parmis les trolls en attente (leur date_post =:= undefined) -% Un message est posté par 'Sys' et le troll elu est lié à ce message -% met à jour sa date de post. -% renvoie plus_de_trolls si il n'y a aucun troll en attente. -elire_troll() -> - {A1,A2,A3} = now(), - random:seed(A1, A2, A3), - mnesia:transaction( - fun() -> - case e(q([T || T <- mnesia:table(troll), T#troll.date_post =:= undefined]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [] -> - plus_de_trolls; - Trolls -> - Troll = lists:nth(random:uniform(length(Trolls)), Trolls), - Troll2 = Troll#troll{date_post = now()}, - mnesia:write(Troll2), - nouveau_message_sys("Troll de la semaine : ", Troll2#troll.id) - end - end - ). - - -% Renvoie l'id du message associé au troll dont l'id est donnée. -% Renvoie undefined si il n'y en a pas. -message_id_associe(Troll_id) -> - resultat_transaction(mnesia:transaction( - fun() -> - case e(q([M#minichat.id || M <- mnesia:table(minichat), M#minichat.troll_id =:= Troll_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of - [Id] -> Id; - _ -> undefined - end - end - )). - - -% Renvoie le résultat d'une transaction (en décomposant le tuple fournit) -resultat_transaction({_, T}) -> - T. - - -% Retourne la difference entre deux timestamp (erlang:now()) en miliseconde -delta_date_ms(D1, D2) -> - 1000000000 * abs(element(1, D1) - element(1, D2)) + 1000 * abs(element(2, D1) - element(2, D2)) + trunc(abs(element(3, D1) - element(3, D2)) / 1000). - -% Retourne la différence entre deux timestamp (erlang:now) en minutes -delta_date_minute(D1, D2) -> - trunc(delta_date_ms(D1, D2) / 1000 / 60). - - -% Renvoie un nouvel id pour une table donnée -nouvel_id(Table) -> + +% Renvoie tous les trolls +trolls() -> + resultat_transaction(mnesia:transaction( + fun() -> + e(qlc:keysort(#troll.id, q([T || T <- mnesia:table(troll)])), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) + end + )). + + +% Renvoie les trolls manquants posté après Last_id. +trolls(Last_id) -> + resultat_transaction(mnesia:transaction( + fun() -> + e(qlc:keysort(#troll.id, q([T || T <- mnesia:table(troll), T#troll.id > Last_id, T#troll.date_post =:= undefined])), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) + end + )). + + + % Crée un nouveau troll. + % Renvoie l'id du nouveau troll + % ou max_troll_reached_per_user si le nombre de troll posté par l'utilisateur max a été atteind + % ou max_troll_reached si le nombre de troll posté max a été atteind + % ou user_unknown +put_troll(User_id, Content) -> + resultat_transaction(mnesia:transaction( + fun() -> + % control le nombre de troll déjà posté + Nb_troll_poste_par_user = length(e(q( + [ + E#troll.id || E <- mnesia:table(troll), + E#troll.id_user =:= User_id, + E#troll.date_post =:= undefined + ] + ), [{tmpdir, ?KEY_SORT_TEMP_DIR}])), + Nb_troll_poste_total = length(e(q( + [ + E#troll.id || E <- mnesia:table(troll), + E#troll.date_post =:= undefined + ] + ), [{tmpdir, ?KEY_SORT_TEMP_DIR}])), + User = user_by_id(User_id), + case User of + {ok, _} -> + if Nb_troll_poste_par_user >= ?NB_MAX_TROLL_WAITING_BY_USER -> + max_troll_reached_per_user; + Nb_troll_poste_total >= ?NB_MAX_TROLL_WAITING -> + max_troll_reached; + true -> + Id = nouvel_id(minichat), + mnesia:write(#troll{id = Id, id_user = User_id, date_create = now(), content = Content}), + Id + end; + _ -> + user_unknown + end + end + )). + + +% renvoie ok | erreur +mod_troll(Troll_id, Content) -> + mnesia:transaction( + fun() -> + case mnesia:wread({troll, Troll_id}) of + [Troll = #troll{date_post = undefined}] -> + mnesia:write(Troll#troll{content = Content}); + _ -> + mnesia:abort("mod_troll: Troll inconnu ou déjà posté") + end + end + ). + + +del_troll(Troll_id) -> + mnesia:transaction( + fun() -> + case mnesia:wread({troll, Troll_id}) of + [#troll{date_post = undefined}] -> + mnesia:delete({troll, Troll_id}); + _ -> + mnesia:abort("mod_troll: Troll inconnu ou déjà posté") + end + end + ). + + +troll_by_id(Troll_id) -> + resultat_transaction(mnesia:transaction( + fun() -> + case e(q([T || T <- mnesia:table(troll), T#troll.id =:= Troll_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [T] -> {ok, T}; + _ -> + erreur + end + end + )). + + +% Renvoie le troll actuel qui se trouve sur la page principale. +% Renvois aucun si pas de troll courant. +current_troll() -> + resultat_transaction(mnesia:transaction( + fun() -> + C = cursor(qlc:keysort(#troll.date_post, q([T || T <- mnesia:table(troll), T#troll.date_post =/= undefined]), [{order, descending}]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]), + R = case qlc:next_answers(C, 1) of + [T] -> T; + _ -> aucun + end, + qlc:delete_cursor(C), + R + end + )). + + +% Elit un troll au hasard parmis les trolls en attente (leur date_post =:= undefined) +% Un message est posté par 'Sys' et le troll elu est lié à ce message +% met à jour sa date de post. +% renvoie plus_de_trolls si il n'y a aucun troll en attente. +elire_troll() -> + {A1,A2,A3} = now(), + random:seed(A1, A2, A3), + mnesia:transaction( + fun() -> + case e(q([T || T <- mnesia:table(troll), T#troll.date_post =:= undefined]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [] -> + plus_de_trolls; + Trolls -> + Troll = lists:nth(random:uniform(length(Trolls)), Trolls), + Troll2 = Troll#troll{date_post = now()}, + mnesia:write(Troll2), + nouveau_message_sys("Troll de la semaine : ", Troll2#troll.id) + end + end + ). + + +% Renvoie l'id du message associé au troll dont l'id est donnée. +% Renvoie undefined si il n'y en a pas. +message_id_associe(Troll_id) -> + resultat_transaction(mnesia:transaction( + fun() -> + case e(q([M#minichat.id || M <- mnesia:table(minichat), M#minichat.troll_id =:= Troll_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of + [Id] -> Id; + _ -> undefined + end + end + )). + + +% Renvoie le résultat d'une transaction (en décomposant le tuple fournit) +resultat_transaction({_, T}) -> + T. + + +% Retourne la difference entre deux timestamp (erlang:now()) en miliseconde +delta_date_ms(D1, D2) -> + 1000000000 * abs(element(1, D1) - element(1, D2)) + 1000 * abs(element(2, D1) - element(2, D2)) + trunc(abs(element(3, D1) - element(3, D2)) / 1000). + +% Retourne la différence entre deux timestamp (erlang:now) en minutes +delta_date_minute(D1, D2) -> + trunc(delta_date_ms(D1, D2) / 1000 / 60). + + +% Renvoie un nouvel id pour une table donnée +nouvel_id(Table) -> mnesia:dirty_update_counter(counter, Table, 1). \ No newline at end of file diff --git a/modules/include/euphorik_bd.hrl b/modules/include/euphorik_bd.hrl index ad5c04c..b779583 100755 --- a/modules/include/euphorik_bd.hrl +++ b/modules/include/euphorik_bd.hrl @@ -1,84 +1,84 @@ -% Copyright 2008 Grégory Burri -% -% This file is part of Euphorik. -% -% Euphorik is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% Euphorik is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with Euphorik. If not, see . -% -% @author GBurri +% Copyright 2008 Grégory Burri +% +% This file is part of Euphorik. +% +% Euphorik is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Euphorik is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Euphorik. If not, see . +% +% @author GBurri % Version de la BD -define(VERSION_BD, 2). -% Pour générer des id --record(counter, - { - key, - value - }). +% Pour générer des id +-record(counter, + { + key, + value + }). -% Mémorse toutes les propriétés, entre autre la version des données +% Mémorse toutes les propriétés, entre autre la version des données -record(proprietes, { nom, valeur - }). + }). - -% décrit un enregistrement d'un message + +% décrit un enregistrement d'un message -record(minichat, - { - id, % integer + { + id, % integer auteur_id, % -> #user.id date, % erlang:now() - pseudo, % chaine de caractère - contenu, % chaine de caractère - troll_id = undefined, % l'id du troll associé correspondant - racine_id = undefined % la racine, par défaut correspond à l'id du message - }). + pseudo, % chaine de caractère + contenu, % chaine de caractère + troll_id = undefined, % l'id du troll associé correspondant + racine_id = undefined % la racine, par défaut correspond à l'id du message + }). - -% type bag -% 'repondant' repond à 'cible' --record(reponse_minichat, - { - repondant, % -> #minichat.id - cible % -> #minichat.id - }). + +% type bag +% 'repondant' repond à 'cible' +-record(reponse_minichat, + { + repondant, % -> #minichat.id + cible % -> #minichat.id + }). + - --record(user, - { - id, - cookie, % string() +-record(user, + { + id, + cookie, % string() pseudo = [], % string() login = [], % string() password = [], % string() (md5) - email = [], % string() - date_creation, % erlang:now() - date_derniere_connexion, % erlang:now(), est mis à jour lors de n'importe quelle activitée (envoie de message par exemple) + email = [], % string() + date_creation, % erlang:now() + date_derniere_connexion, % erlang:now(), est mis à jour lors de n'importe quelle activitée (envoie de message par exemple) css = [], % string() nick_format = nick, %atom(), peut valoir 'nick', 'login' ou 'nick_login' view_times = true, view_tooltips = true, message_order = reverse, % can be normal or reverse - indice_flood = 0, % integer() est incrémenté lorsque l'utilisateur envoie trop rapidement des messages. + indice_flood = 0, % integer() est incrémenté lorsque l'utilisateur envoie trop rapidement des messages. conversations = [], % [integer()], la liste des messages correspondant au conversation ek_master = false, - last_ip = undefined % integer(), undefined si inconnu + last_ip = undefined % integer(), undefined si inconnu }). @@ -89,9 +89,9 @@ ban = undefined, % la date du dernier bannissement ban_duration = 0, % le temps de ban en minute nb_try_register = 0, - nb_try_login = 0, % pour l'instant pas utilisé + nb_try_login = 0, % pour l'instant pas utilisé date_last_try_register, - date_last_try_login % pour l'instant pas utilisé + date_last_try_login % pour l'instant pas utilisé }). @@ -100,7 +100,7 @@ id, id_user, date_create, % erlang:now() - date_post = undefined, % date à laquelle le troll est affiché sur la page principale. undefined initialement puis erlang:now() quand affiché - content % chaine de caractère + date_post = undefined, % date à laquelle le troll est affiché sur la page principale. undefined initialement puis erlang:now() quand affiché + content % chaine de caractère }). \ No newline at end of file diff --git a/modules/include/euphorik_defines.hrl b/modules/include/euphorik_defines.hrl index c0791b5..bf6ec5b 100755 --- a/modules/include/euphorik_defines.hrl +++ b/modules/include/euphorik_defines.hrl @@ -1,51 +1,51 @@ -% Copyright 2008 Grégory Burri -% -% This file is part of Euphorik. -% -% Euphorik is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% Euphorik is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with Euphorik. If not, see . - - -% Le temps d'attente après une erreur de login (mauvais login/pass) : une demie seconde -% Permet d'éviter (limiter) les attaques par dictionnaire +% Copyright 2008 Grégory Burri +% +% This file is part of Euphorik. +% +% Euphorik is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% Euphorik is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with Euphorik. If not, see . + + +% Le temps d'attente après une erreur de login (mauvais login/pass) : une demie seconde +% Permet d'éviter (limiter) les attaques par dictionnaire -define(TEMPS_ATTENTE_ERREUR_LOGIN, 500). % ms -% Un message est considéré comme du spam s'il est posté 1 seconde ou moins après le dernier posté +% Un message est considéré comme du spam s'il est posté 1 seconde ou moins après le dernier posté -define(DUREE_SPAM, 1000). % ms % Lorsque l'indice de spam d'un utilisateur atteind cette valeur alors il ne peut plus poster pendant un moment -define(INDICE_SPAM_MAX, 6). -% Un utilisateur ayant trop spamé est bloqué pendant ce temps +% Un utilisateur ayant trop spamé est bloqué pendant ce temps -define(DUREE_BLOCAGE_SPAM, 20000). % ms -% le temps qu'une ip est bannie après avoir voulu s'etre enregistré trop de fois trop rapidement +% le temps qu'une ip est bannie après avoir voulu s'etre enregistré trop de fois trop rapidement -define(TEMPS_BAN_FLOOD_REGISTER, 60 * 60 * 1000). % 1 heure : en ms % le temps entre deux tentatives de register pour compter un flood -define(TEMPS_FLOOD_REGISTER, 1500). % 1500 ms -% après 5 flood l'ip fautive est considérée comme bannie +% après 5 flood l'ip fautive est considérée comme bannie -define(NB_MAX_FLOOD_REGISTER, 5). -% le nombre max de troll qui peuvent être en attente d'être posté (tous les utilisateurs réunis) +% le nombre max de troll qui peuvent être en attente d'être posté (tous les utilisateurs réunis) -define(NB_MAX_TROLL_WAITING, 10). -% chaque admin peut proposer 1 seul troll à la fois +% chaque admin peut proposer 1 seul troll à la fois -define(NB_MAX_TROLL_WAITING_BY_USER, 2). -% Le jour ainsi que l'heure à laquelle est élu un nouveau troll (lundi à 3 heure du mat) +% Le jour ainsi que l'heure à laquelle est élu un nouveau troll (lundi à 3 heure du mat) -define(JOUR_ELECTION_TROLL, 1). % 1 = lundi -define(HEURE_ELECTION_TROLL, 3). % 3 heure du matin -% Le dossier utilisé pour le trie (qlc:keysort()) +% Le dossier utilisé pour le trie (qlc:keysort()) -define(KEY_SORT_TEMP_DIR, "/tmp"). diff --git a/nbproject/private/config.properties b/nbproject/private/config.properties new file mode 100644 index 0000000..e69de29 diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties new file mode 100644 index 0000000..6691e30 --- /dev/null +++ b/nbproject/private/private.properties @@ -0,0 +1,3 @@ +file.reference.modules-erl=/home/gburri/projets/euphorik/trunk/modules/erl +file.reference.modules-include=/home/gburri/projets/euphorik/trunk/modules/include +platform.active=default diff --git a/nbproject/private/private.xml b/nbproject/private/private.xml new file mode 100644 index 0000000..c1f155a --- /dev/null +++ b/nbproject/private/private.xml @@ -0,0 +1,4 @@ + + + + diff --git a/nbproject/project.properties b/nbproject/project.properties new file mode 100644 index 0000000..93e29d0 --- /dev/null +++ b/nbproject/project.properties @@ -0,0 +1,12 @@ +file.reference.modules-erl=modules/erl +file.reference.modules-include=modules/include +include.dir=${file.reference.modules-include} +javac.classpath= +main.file= +ruby.includejava=false +src.css.dir=css +src.dir=${file.reference.modules-erl} +src.doc.dir=doc +src.js.dir=js +src.pages.dir=pages +src.tools.dir=tools diff --git a/nbproject/project.xml b/nbproject/project.xml new file mode 100644 index 0000000..fa0f3ba --- /dev/null +++ b/nbproject/project.xml @@ -0,0 +1,20 @@ + + + org.netbeans.modules.erlang.project + + + euphorik + + + + + + + + + + + + + +