MOD passage de quelques fichier de iso-8859-1 à UTF8
authorGreg Burri <greg.burri@gmail.com>
Sat, 21 Jun 2008 10:01:06 +0000 (10:01 +0000)
committerGreg Burri <greg.burri@gmail.com>
Sat, 21 Jun 2008 10:01:06 +0000 (10:01 +0000)
ADD dossier de projet pour netbeans (erlybird)

13 files changed:
doc/description.txt
doc/installation.txt
doc/protocole3.txt
doc/technique.txt
js/pageMinichat.js
modules/erl/euphorik_bd.erl
modules/include/euphorik_bd.hrl
modules/include/euphorik_defines.hrl
nbproject/private/config.properties [new file with mode: 0644]
nbproject/private/private.properties [new file with mode: 0644]
nbproject/private/private.xml [new file with mode: 0644]
nbproject/project.properties [new file with mode: 0644]
nbproject/project.xml [new file with mode: 0644]

index 11e1205..94eee1a 100644 (file)
-== En bref ==\r
-Euphorik est un site web communautaire principalement basé sur un système d'échange de messages instantanés.\r
-Attention: la description ici ne correspond pas à l'état actuel du projet mais à un but à atteindre.\r
-\r
-== Philosophie ==\r
-Euphorik est un site communaire de niveau supérieur (un truc qui n'existe pas et qui n'existera probablement jamais).\r
-N'importe qui peut poster des messages ou des trolls (un troll étant un super message persistant à caractère trollifique).\r
-Il est possible de s'enregistrer pour garder son identité et sauvegarder certains paramètres.\r
-Pas besoin d'être authentifier pour poster des messages\r
-Il faut être enregistré pour poster des trolls (ouais bein quant on troll on assume)\r
-Il n'y a qu'un seul canal par troll (channel au sens IRC).\r
-Un message peut répondre à un ou plusieurs autres messages, ceci crée automatiquement des arbres de conversation (au sein d'un troll).\r
-Ces arbres de conversation peuvent être extraient de la conversation principal et affichés séparement (toujours au sein d'un troll).\r
-Il existe des êtres supérieures qui ont de grands pouvoirs, ce sont les EkMaster ou [EM] (les admins quoi).\r
-L'interface doit être sobre, simple et un peu retro :)\r
-Il est interdit d'utiliser des technos pourries comme PHP.\r
-\r
-\r
-== Détails ==\r
-=== Pages ===\r
-* Main : Présente les trolls de la semaine\r
-* 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.\r
-* People : Permet de rechercher une personne et d'afficher sa page, en particulier ses trolls.\r
-* Profile : Permet d'accèder à ses données. C'est à partir de cette page que l'on peut poster des trolls.\r
-* About : description du site (Faq et cie..)\r
-\r
-=== Le Troll ===\r
-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.\r
-Il existe un troll principal concernant le chat principal.\r
-Un troll peut être édité par son auteur.\r
-N'importe qui peut voir l'historique des éditions.\r
-Il est possible de plusser ou moinsser un troll.\r
-Un troll possède de 0 à n tag (mot-clef).\r
-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)\r
-Les trolls sont également présentés sur le profile du proprio du troll (par ordre anti-chronologique)\r
-\r
-=== Le troll de la semaine ===\r
-Sur la page principale appelé 'chat' il existe un troll qui sera affiché une semaine appellé "troll de la semaine".\r
-Le troll de la semaine est posté par les admins.\r
-Les admins voient les prochains trolls en attente, le nombre en attente est limité 10.\r
-Un admin peut ajouter un troll de la semaine. Il ne peut pas posséder plus d'un troll en attente.\r
-Le troll de la semaine change le lundi à 3h00 du mat' s'il en existe un en attente. Il est choisi au hasard.\r
-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.\r
-\r
-=== Le message ===\r
-Un message répond à un troll et peut répondre à d'autres messages de ce troll.\r
-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"\r
-\r
-\r
-=== Admin ===\r
-L'admin propose des trolls de la semaine, il a le statut de EM (EkMaster)\r
-\r
-\r
-== Reflexions ==\r
-Les types d'information du plus éphémère au plus persistant.\r
- * Plussage/moinssage\r
-   * Message\r
- * Message (1-1)\r
-   * Blog\r
-   * Forum\r
-   * Article\r
- * Question (1-1) | (1-n)\r
-   * Forum\r
-   * Message\r
- * Billet (1-n)\r
-   * Blog\r
- * Article (1-n) | (n-n)\r
-   * Wikipedia\r
-   \r
-   \r
-Moyen de communication sur le net :\r
-\r
-* Réseaux sociaux (facebook et cie)\r
-   + Orienté profile\r
-   + Liste d'amis\r
-   + Possibilité de mettre des infos personnels + photos\r
-\r
-* Vidéo (youtube et cie)\r
-   + Orienté vidéo\r
-\r
-* Reddit/Digg\r
-   * Aggrégateur de news/billet de blog/article\r
-   * L'ordre des informations peut changer (en fonction de la note)\r
-\r
-* Blog\r
-   * Orienté billet\r
-   * Géré par une seule personne\r
-   * Système de messages\r
-   + Structuration et recherche par tag (chaque billet possède un ou plusieurs tags)\r
-\r
-* Forum (phpBB, vBulettin, mesDiscussions, etc.)\r
-   * Orienté sujet\r
-   * Organisation hiérachique en thémes, p.e. : Hardware/HDD\r
-   * L'ordre des sujets ne correspond pas à leur date d'écriture mais à la date du dernier message\r
-   * Edition/correction possible\r
-   * Les "réponses" ne sont pas modérer par l'auteur du sujet\r
-   - Pas de système de plussage\r
-   - Par forcément d'arbre de réponses, obligation de quoter -> bordelique\r
-   - Le topic a souvent tendance à dériver\r
-\r
-* Chat (http://www.phpfreechat.net, http://bouchot.org, etc..)\r
-   * Orienté message\r
-   * Ordre figé\r
-   + Scalable grace aux channels\r
-   + Communication temps réel\r
-   - Ca peut devenir le bordel, difficile de suivre\r
-   - L'information est éphemère ou difficilement réutilisable\r
-   - Aucune hiérarchie ou structure en dehors des channels\r
+== 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
index 4f71285..57bb630 100644 (file)
@@ -1,79 +1,79 @@
--- Description de l'installation de Euphorik --\r
-\r
-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.\r
-\r
-* On admet que le dossier de base est "/euphorik".\r
-* On admet que l'utilisateur courant est "toto" et qu'il possède "/euphorik"\r
-* Tout ce qui commence par un '$' correspond à une ligne de commande tapée dans le shell de l'OS.\r
-* Tout ce qui commence par un '>' correspond à une instruction dans le shell de erlang.\r
-\r
-\r
-1. Installer Yaws\r
-   a) $apt-get install yaws\r
-\r
-2. Configurer Yaws\r
-   a) Ajouter les lignes suivantes dans /etc/yaws/yaws.conf :\r
-      - "ebin_dir = /euphorik/modules/ebin"\r
-      - "include_dir = /euphorik/modules/include"\r
-   b) Le serveur virtuel est définit comme ceci dans /etc/yaws/conf.d/localhost.conf :\r
-      <server localhost>\r
-         port = 8081\r
-         listen = 0.0.0.0\r
-         docroot = /euphorik\r
-         allowed_scripts = [yaws]\r
+-- 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 :
+      <server localhost>
+         port = 8081
+         listen = 0.0.0.0
+         docroot = /euphorik
+         allowed_scripts = [yaws]
          appmods = <request, euphorik_requests>
-         start_mod = euphorik_daemon\r
-      </server>\r
-   c) Editer '/etc/init.d/yaws' et remplacer cette ligne :\r
-      script="$DAEMON -I $YAWS_ID $@"\r
-      par celle ci :\r
-      script="$DAEMON --sname yaws --mnesiadir \"/euphorik/BD\" -I $YAWS_ID $@"\r
-      FIXME : trouver une méthode plus élégante.\r
-\r
-3. Créer la base de donnée\r
-   a) Arreter Yaws (en root) :\r
-      $/etc/init.s/yaws stop\r
-   b) Lancer un noeud Erlang\r
-      - Se placer dans le répertoire /euphorik/modules/ebin\r
-      - Executer : \r
-         $erl -sname yaws -mnesia dir '"/euphorik/BD"'\r
-   c) Charger le module :\r
-      >l(euphorik_bd).\r
-   d) Créer la base :\r
-      >euphorik_bd:create().\r
-   e) Démarrer Yaws (en root) :\r
-      $/etc/init.s/yaws start\r
-   \r
-4. Adminisatration du site Euphorik\r
-   a) Connexion au noeud "yaws"\r
-         erl -sname gb\r
-      puis dans la console :\r
-         CTRL-G\r
-         r yaws@overnux\r
-         c 2\r
-      Pour plus d'infos : http://www.ejabberd.im/interconnect-erl-nodes\r
-      Il est possible de connecter un shell directement sur le noeud de yaws comme ceci :\r
-         erl -sname gb -remsh yaws@overnux\r
-      \r
-   b) Utiliser les outils des modules\r
-      - Par exemple :\r
-         >euphorik_minichat:messages(10).\r
-         pour voir les 10 derniers messages   \r
-      \r
-   c) Ancienne méthode de connexion (plus compliqué)\r
-      - 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.\r
-         (si le cookie de yaws est modifié il faut relancer yaws).\r
-      - Se placer dans le répertoire /euphorik/modules/ebin\r
-      - Executer : \r
-         $erl -sname toto\r
-         où "toto" est le nom du noeud (tout sauf "yaws")\r
-      - Charger le module du minichat :\r
-         >l(euphorik_minichat)\r
-      - Se connecter au noeud yaws :\r
-         >euphorik_minichat:connect()\r
-         la valeur retournée doit être : {ok,[yaws@overnux]}\r
-         \r
-   d) Informations sur la mémoire consommée :\r
-      Mémoire totale (ko) :\r
-          trunc(element(2, lists:nth(1, memory())) / 1024).\r
-      voir c:i() également\r
+         start_mod = euphorik_daemon
+      </server>
+   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
index 8c8d253..e89b357 100644 (file)
@@ -2,38 +2,38 @@ Euphorik - Protocole v3
 -----------------------
 
 == Introduction ==
-Ce document a pour but de décrire la communication client-serveur du site euphorik.\r
-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'.
-\r
+
 
 == Principes ==
-Enregistrement:\r
- * 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:\r
- * L'authentification (login) se fait soit par un couple <login, mot de passe> soit à l'aide d'un cookie.\r
- * Permet de récupérer les données d'un profile
+Authentification:
+ * L'authentification (login) se fait soit par un couple <login, mot de passe> 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 :
 <error>
    {
       "reply" : "error",
       "error_message" : "blabla"
    }
    
-Message ok générique :
+Message ok générique :
 <ok>
    {
       "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\r
+ou
    {
-      "reply" : "register" | "authentification",\r
-      "status" : "auth_registered",\r
-      "cookie" : "LKJDLAKSJBFLKASN",\r
-      "id" : 193,\r
-      "nick" : "Paul",\r
-      "login" : "paul49",\r
-      "email" : "paul@pierre.com",\r
+      "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,\r
+      "view_tooltips" : true | false,
       // "main_page" : 1,
       "conversations" : [3, 8],
-      "ek_master" : true | false\r
+      "ek_master" : true | false
    }
\r
  
 === Logout ===
-c -> s\r
-   {\r
-      "action" : "logout",\r
-      "cookie" : "LKJDLAKSJBFLKASN"\r
+c -> s
+   {
+      "action" : "logout",
+      "cookie" : "LKJDLAKSJBFLKASN"
    }
\r
  
 === Profile ===
-c -> s\r
-   {\r
-      "action" : "set_profile",\r
-      "cookie" : "LKJDLAKSJBFLKASN",\r
-      "login" : "paul49",\r
-      "password" : "IJKJDHHSAD9081238",\r
-      "nick" : "Paul",\r
-      "email" : "paul@pierre.com",\r
+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,\r
+      "view_tooltips" : true | false,
       "main_page" : 1,
-      "conversations" : [3, 8]\r
-   }\r
+      "conversations" : [3, 8]
+   }
       
 s -> c
    <ok>
@@ -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\r
-   {\r
+c -> s
+   {
       "action" : "wait_event",
-      "page" : "chat"\r
-      "cookie" : "LKJDLAKSJBFLKASN",\r
+      "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)
          }
-      ]\r
+      ]
    }
  
-s -> c\r
-La première conversation est la principale (main).\r
-L'ordre des conversation est le même que celui des données de l'utilisateur.\r
-Le format de la date n'est pas formel.\r
-   {\r
-      "reply" : "new_message",\r
-      "conversations" : [\r
-         {\r
-            "last_page" : true | false,\r
+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" : [
-               {\r
+               {
                   "id" : 54,
-                  "user_id" : 344,\r
-                  "date" : "Hier 17:26:54",\r
-                  "system" : true | false,\r
-                  "owner" : true | false,\r
-                  "answered" : true | false,\r
-                  "is_a_reply" : true | false,\r
-                  "nick" : "Paul",\r
-                  "login" : "paul_22",\r
+                  "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,\r
-                  "answer_to" : [\r
-                     { "id" : 123, "nick" : "Pierre", "login" : "pierre_45" }\r
+                  "root" : 453,
+                  "answer_to" : [
+                     { "id" : 123, "nick" : "Pierre", "login" : "pierre_45" }
                   ]
                   "ek_master" : true | false
-               }\r
-            ]\r
+               }
+            ]
          }
-         ...\r
-      ]\r
+         ...
+      ]
    }
-ou\r
-   {\r
-      "reply" : "message_updated",\r
-      "message_id" : 123,\r
-      "content" : "Salut +++ poulpe"\r
+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"
    }
-\r
-\r
-=== Envoie d'un troll ===\r
-c -> s\r
-   {\r
-      "action" : "put_troll",\r
-      "cookie" : "LKJDLAKSJBFLKASN",\r
-      "content" : "Un bon troll velu !"\r
+
+
+=== 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
    <error>
    
-   \r
+   
 === Modification d'un troll ===
 c -> s
    {
@@ -264,7 +264,7 @@ s -> c
 ou
    <error>
    
-   \r
+   
 === 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.\r
+Le client envoie un message, le message peut répondre à un certain nombre d'autres messages.
+"answer_to" n'est pas obligatoire.
 
-c -> s\r
-   {\r
-      "action" : "put_message",\r
-      "cookie" : "LKJDLAKSJBFLKASN",\r
-      "nick" : "Paul",\r
-      "content" : "Bonjour",\r
-      "answer_to" : [ 345, 532, ... ]\r
+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
    <ok>
 ou
    <error>
-\r
\r
-=== Ajout d'une correction d'un messages ===\r
-Le client envoie un correctif sous la forme de texte supplémentaire à appondre au dernier messages.\r
-Le message est appondu avec un " +++ " devant, par exemple :\r
-> Gnome c'est mieux que KDE +++ Euh non ok, c'est faux\r
-\r
-c -> s\r
-   {\r
-      "action" : "correction",\r
-      "cookie" : "LKJDLAKSJBFLKASN",\r
-      "content" : "Euh non ok, c'est faux"\r
-   }\r
-   \r
-s -> c\r
-   {\r
-      "reply" : "correction",\r
-      "status" : "ok" | "error",\r
-      "message_error" : "blabla"\r
+
+=== 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"
    } 
index 74a6360..458c81f 100644 (file)
@@ -3,8 +3,8 @@ Euphorik - doc technique
 
 == euphorik.js ==
 Sequences :
-   * Chargement d'une page\r
-   \r
+   * 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
index b09b4ca..7ad6c5f 100755 (executable)
@@ -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()
                }
-            )\r
+            )
       }
    )
    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)
-            {\r
-               // "repondu" et "reponse" sont prioritaitres à "proprietaire"\r
-               // contrairement à la vue normale (sans mise en évidence d'une conversation)\r
-               case 3 :\r
-                  jq.addClass("repondu")\r
-                  break;\r
-               case 2 :\r
-                  jq.addClass("reponse")\r
+            {
+               // "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)
 {   
index 4c27577..a0ffc3a 100755 (executable)
 %\r
 % You should have received a copy of the GNU General Public License\r
 % along with Euphorik.  If not, see <http://www.gnu.org/licenses/>.\r
-% 
-% 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.
+% \r
+% Ce module permet de gérer les données persistantes lié au site d'euphorik.ch.\r
+% Il permet d'ajouter des message, de demande les messages sur une page donnée, etc..\r
+% Ce module utilise une base mnesia.\r
 % @author G.Burri\r
-
-
+\r
+\r
 -module(euphorik_bd).\r
--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([\r
+   % gestion :\r
+   create/0,\r
+   connect/0,\r
+   connect/1,\r
+   reset/0,\r
+   \r
+   % users :\r
+   nouveau_user/2,\r
+   nouveau_user/3,\r
+   set_profile/10,\r
+   update_date_derniere_connexion/1,\r
+   update_ip/2,\r
+   update_pseudo_user/2,\r
+   print_users/0,\r
+   print_users/1,\r
+   print_user/1,\r
+   user_by_cookie/1,   \r
+   user_by_id/1,   \r
+   user_by_login/1,\r
+   user_by_login_password/2,\r
+   user_by_mess/1,\r
+   toggle_ek_master/1,\r
+   css_from_user_cookie/1,\r
+   \r
+   % messages :e\r
+   nouveau_message/3,\r
+   nouveau_message_sys/1,\r
+   nouveau_message_sys/2,\r
+   messages/1,\r
    messages/2,\r
-   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,\r
+   message_by_id/1,\r
+   messages_by_ids/1,\r
+   message_existe/1,\r
+   reponses/0,\r
+   parents/1,\r
+   enfants/1,\r
+   est_une_reponse_a_user/2,\r
+   a_repondu_a_message/2,\r
+   possede_message/2,\r
+   \r
+   % ip :\r
+   list_ban/0,\r
+   ban/2,\r
+   deban/1,\r
+   est_banni/1,\r
+   can_register/1,\r
+   \r
+   % trolls :\r
+   trolls/0,\r
+   trolls/1,\r
+   put_troll/2,\r
+   mod_troll/2,\r
+   del_troll/1,\r
+   troll_by_id/1,\r
+   current_troll/0,\r
+   elire_troll/0,\r
+   message_id_associe/1,\r
+   \r
+   % utiles :\r
    resultat_transaction/1\r
-]).
+]).\r
 -import(qlc, [e/2, q/1, cursor/2]).\r
--include("../include/euphorik_bd.hrl").
+-include("../include/euphorik_bd.hrl").\r
 -include("../include/euphorik_defines.hrl").\r
 -include_lib("stdlib/include/qlc.hrl").\r
-
-
-% 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]).
-
-
+\r
+\r
+% Instructions pour créer une nouvelle base : \r
+% $erl -sname yaws -mnesia dir '"/projets/euphorik/BD"'\r
+% voir doc/installation.txt\r
+% >l(euphorik_bd).\r
+% >euphorik_bd:create().\r
+create() ->\r
+   mnesia:stop(),\r
+   mnesia:delete_schema([node()]),\r
+   mnesia:create_schema([node()]), % nécessaire pour les tables sur disc\r
+   mnesia:start(),\r
+   create_tables(),\r
+   reset().\r
+   \r
+create_tables() ->\r
+   mnesia:create_table(counter, [\r
+      {attributes, record_info(fields, counter)},\r
+      {disc_copies, [node()]}\r
+   ]),\r
+   mnesia:create_table(proprietes, [\r
+      {attributes, record_info(fields, proprietes)},\r
+      {disc_copies, [node()]}\r
+   ]),\r
+   mnesia:create_table(minichat, [\r
+      {attributes, record_info(fields, minichat)},\r
+      {index, [auteur_id, troll_id]},\r
+      {disc_copies, [node()]}\r
+   ]),\r
+   mnesia:create_table(reponse_minichat, [\r
+      {type, bag},\r
+      {attributes, record_info(fields, reponse_minichat)},\r
+      {index, [cible]},\r
+      {disc_copies, [node()]}\r
+   ]),\r
+   mnesia:create_table(user, [\r
+      {attributes, record_info(fields, user)},\r
+      {index, [cookie, login]},\r
+      {disc_copies, [node()]}\r
+   ]),\r
+   mnesia:create_table(ip_table, [\r
+      {attributes, record_info(fields, ip_table)},\r
+      {disc_copies, [node()]}\r
+   ]),\r
+   mnesia:create_table(troll, [\r
+      {attributes, record_info(fields, troll)},\r
+      {index, [date_post]},\r
+      {disc_copies, [node()]}\r
+   ]).\r
+   \r
+   \r
+% Connexion à la base de données de yaws sur overnux\r
+connect() ->\r
+   connect(yaws@flynux).\r
+connect(Node) ->\r
+   mnesia:start(),\r
+   mnesia:change_config(extra_db_nodes, [Node]).\r
+\r
+\r
 % Efface tous les users, minichat_reponse et minichat.\r
 reset() ->\r
-   mnesia:clear_table(counter),
+   mnesia:clear_table(counter),\r
    mnesia:clear_table(proprietes),\r
    mnesia:clear_table(user),\r
    mnesia:clear_table(reponse_minichat),\r
-   mnesia:clear_table(minichat),
-   mnesia:clear_table(troll),
+   mnesia:clear_table(minichat),\r
+   mnesia:clear_table(troll),\r
    mnesia:clear_table(ip_table),\r
    % crée l'utilisateur root\r
-   mnesia:transaction(fun() ->
+   mnesia:transaction(fun() ->\r
       mnesia:write(#proprietes{nom = version, valeur = ?VERSION_BD}),\r
       User = #user{id = 0, pseudo = "Sys", login = "Sys", date_creation = now(), date_derniere_connexion = now(), ek_master = true},\r
       mnesia:write(User),\r
       User\r
-   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).\r
+\r
+\r
+% Ajoute un nouveau user et le renvoie\r
+nouveau_user(Pseudo, Cookie) ->\r
+   F = fun() ->\r
+      Id = nouvel_id(user),\r
+      User = #user{id = Id, cookie = Cookie, pseudo = Pseudo, date_creation = now(), date_derniere_connexion = now()},\r
+      mnesia:write(User),\r
+      User\r
+   end,\r
+  resultat_transaction(mnesia:transaction(F)).\r
+  \r
+  \r
+% Ajoute un nouveau user et le renvoie\r
+nouveau_user(Login, Password, Cookie) ->\r
+   F = fun() ->\r
+      Id = nouvel_id(user),\r
+      User = #user{id = Id, cookie = Cookie, pseudo = Login, login = Login, password = Password, date_creation = now(), date_derniere_connexion = now()},\r
+      mnesia:write(User),\r
+      User\r
+   end,\r
+  resultat_transaction(mnesia:transaction(F)).\r
+\r
+\r
+% Mise à par Cookie les autres peuvent être undefined ce qui veut dire qu'ils ne seront pas modifié.\r
+set_profile(Cookie, Login, Password, Pseudo, Email, Css, Nick_format, View_times, View_tooltips, Conversations) ->\r
+   if Nick_format =:= nick; Nick_format =:= login; Nick_format =:= nick_login ->\r
+         resultat_transaction(mnesia:transaction(\r
+            fun() ->\r
+               case user_by_cookie(Cookie) of\r
+                  {ok, User} ->\r
+                     case user_by_login(Login) of\r
+                        {ok, U} when Login =/= [], U#user.id =/= User#user.id ->\r
+                           login_deja_pris;\r
+                        _ ->\r
+                           User_modifie = User#user{\r
+                              % TODO : pourquoi ne pas tester avec la valeur "undefined" plutôt qu'avec "is_list" ?\r
+                              % TODO : validation plus strict des données (pas de page négative dans les conv par exemple)\r
+                              login = if is_list(Login) -> Login; true -> User#user.login end,\r
+                              password = if is_list(Password) andalso Password =/= [] -> Password; true -> User#user.password end,\r
+                              pseudo = if is_list(Pseudo) -> Pseudo; true -> User#user.pseudo end,\r
+                              email = if is_list(Email) -> Email; true -> User#user.email end,\r
+                              css = if is_list(Css) -> Css; true -> User#user.css end,\r
+                              nick_format = Nick_format,\r
+                              view_times = View_times,\r
+                              view_tooltips = View_tooltips,\r
+                              conversations = if is_list(Conversations) -> Conversations; true -> User#user.conversations end\r
+                           },\r
+                           mnesia:write(User_modifie),\r
+                           ok\r
+                     end;\r
+                  _ -> erreur\r
+               end\r
+            end\r
+         ));\r
+      true ->\r
+         erreur\r
+   end.\r
+\r
+\r
+% Met à jour la date de la dernière connexion d'un utilisateur à maintenant\r
+update_date_derniere_connexion(User_id) ->\r
+   mnesia:transaction(\r
+      fun() ->\r
+         case mnesia:wread({user, User_id}) of\r
+            [User] ->\r
+               mnesia:write(User#user{date_derniere_connexion = now()});\r
+            _ ->\r
+               mnesia:abort("update_date_derniere_connexion: User inconnu")\r
+          end\r
+      end\r
+   ).   \r
+\r
+\r
+% Met à jour l'ip d'un user\r
+update_ip(User_id, IP) ->\r
+   mnesia:transaction(\r
+      fun() ->\r
+         case mnesia:wread({user, User_id}) of\r
+            [User] ->\r
+               mnesia:write(User#user{last_ip = IP});\r
+            _ ->\r
+               mnesia:abort("update_ip: User inconnu")\r
+          end\r
+      end\r
+   ).   \r
+   \r
+  \r
+% Met à jour le pseudo du user\r
+update_pseudo_user(UserId, Pseudo) ->\r
+   mnesia:transaction(\r
+      fun() ->      \r
+         case mnesia:wread({user, UserId}) of\r
+            [User] when User#user.pseudo =/= Pseudo ->\r
+               mnesia:write(User#user{pseudo = Pseudo});\r
+            _ ->\r
+               mnesia:abort("update_pseudo_user: User inconnu ou pseudo deja à jour")\r
+          end\r
+      end\r
+   ).\r
+   \r
+   \r
+% Affiche N user trié par leur date de dernière connexion.\r
+% Opt est une liste d'option d'affichage :\r
+%  * ekmaster : n'affiche que les admins\r
+print_users(N, Opt) ->\r
+   AfficheQueLesEkMaster = lists:any(fun(O) -> O =:= ekmaster end, Opt),\r
+   resultat_transaction(mnesia:transaction(fun() ->\r
+      C = cursor(\r
+         qlc:keysort(\r
+            #user.date_derniere_connexion, \r
+            if AfficheQueLesEkMaster ->\r
+               q([E || E <- mnesia:table(user), E#user.ek_master =:= true]);\r
+            true ->\r
+               q([E || E <- mnesia:table(user)])\r
+            end,\r
+            [{order, descending}]\r
+         ),\r
+         [{tmpdir, ?KEY_SORT_TEMP_DIR}]\r
+      ),\r
+      Users = qlc:next_answers(C, N),\r
+      lists:foreach(\r
+         fun(U) ->\r
+            print_user(U)\r
+         end,\r
+         Users\r
+      ),\r
+      qlc:delete_cursor(C)\r
+   end)).\r
+   \r
+   \r
+% Affiche tous les users.\r
+print_users(Opt) ->\r
+   print_users(all_remaining, Opt).\r
+\r
+% Affiche tous les users.\r
+print_users() ->\r
+   print_users(all_remaining, []).\r
+   \r
+print_user(User) when is_record(User, user) ->\r
+   #user{id = Id, pseudo = Pseudo, login = Login, ek_master = Ek_master, date_derniere_connexion = Date, last_ip = IP} = User,\r
+   {{Annee, Mois, Jour}, {Heure, Min, _}} = calendar:now_to_local_time(Date),\r
+   io:format(\r
+      % id        pseudo     (login)        IP  Jour  Mois   Année  Heure Minute\r
+      "~4w : ~10.10..s(~10.10..s) ~s ~2w.~2.2.0w.~w - ~2wh~2.2.0w~n",\r
+      [  \r
+         Id,\r
+         if Ek_master -> "*"; true -> "" end ++ Pseudo,\r
+         Login,\r
+         euphorik_common:serialize_ip(IP),\r
+         Jour, Mois, Annee, Heure, Min\r
+      ]\r
+   );\r
+% Affichage d'un user en fonction de son login\r
+print_user(Login) when is_list(Login) ->\r
+   case user_by_login(Login) of\r
+      {ok, User} ->\r
+         print_user(User);\r
+      _ ->\r
+         {erreur, "Login pas trouvé : " ++ Login}\r
+   end;\r
+% Affichage d'un user en fonction de son id\r
+print_user(Id) when is_integer(Id) -> \r
+   case user_by_id(Id) of \r
+      {ok, User} ->\r
+         print_user(User);\r
+      _ ->\r
+         {erreur, "Id pas trouvé : " ++ integer_to_list(Id)}\r
+   end.\r
+   \r
+\r
+% Est-ce qu'un utilisateur existe en fonction de son cookie ?\r
+% Renvoie {ok, User} ou erreur\r
+user_by_cookie(Cookie) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         case e(q([E || E <- mnesia:table(user), E#user.cookie =:= Cookie]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of\r
+            [User] -> {ok, User};\r
+            _ -> erreur\r
+         end\r
+      end\r
+   )).\r
+   \r
+   \r
+user_by_id(ID) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         case e(q([E || E <- mnesia:table(user), E#user.id =:= ID]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of\r
+            [User] -> {ok, User};\r
+            _ -> erreur\r
+         end\r
+      end\r
+   )).\r
    \r
    \r
 user_by_login(Login) ->\r
@@ -370,552 +370,552 @@ user_by_login(Login) ->
          end\r
       end\r
    )).\r
-   
-   
-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
+   \r
+   \r
+toggle_ek_master(User_id) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         Users = e(q([E || E <- mnesia:table(user), E#user.id =:= User_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]),\r
+         case Users of\r
+            [User] ->\r
+               mnesia:write(User#user{ek_master = not User#user.ek_master});\r
+            _ -> erreur\r
+         end\r
+      end\r
+   )).\r
+   \r
+\r
+% Renvoie une chaine représentant le cookie ou undefined si pas trouvé.\r
+css_from_user_cookie(Cookie) ->\r
+   case user_by_cookie(Cookie) of \r
+      {ok, User} ->\r
+         User#user.css;\r
+      _ ->\r
+         undefined\r
+   end.\r
+   \r
+\r
+user_by_login_password(Login, Password) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         case e(q([E || E <- mnesia:table(user), E#user.login =:= Login, E#user.password =:= Password]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of\r
+            [User | _] -> {ok, User};\r
+            _ -> erreur\r
+         end\r
+      end\r
+   )).\r
+   \r
+   \r
+% Renvoie {ok, User} où User est un #user possédant le message donné.\r
+user_by_mess(Id) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         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\r
+            [User | _] -> {ok, User};\r
+            _ -> erreur\r
+         end\r
+      end\r
+   )).\r
+   \r
+   \r
+% Ajoute un message. Repond_A est une liste d'id auquel le message répond\r
+% retourne soit l'id du message soit {erreur, <raison>}.\r
+nouveau_message(Mess, Auteur_id, Repond_A_ids) ->\r
+   % regarde si les id 'Repond_A' existent\r
+   F = fun() ->   \r
+      Repond_A = e(\r
+         q([M || M <- mnesia:table(minichat), lists:member(M#minichat.id, Repond_A_ids)]),\r
+         [{tmpdir, ?KEY_SORT_TEMP_DIR}]\r
+      ),\r
+      Racine_id = case Repond_A of\r
+         [] -> undefined;\r
+         [M | _ ] -> M#minichat.racine_id\r
+      end,\r
+      % est-ce que l'auteur existe ?\r
+      case e(q([E || E <- mnesia:table(user), E#user.id =:= Auteur_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of\r
+         [Auteur] ->\r
+            if length(Repond_A) =/= length(Repond_A_ids) ->\r
+                  {erreur, "Un ou plusieurs messages introuvable"};\r
+               true ->\r
+                  % comparaison entre la date du dernier poste et maintenant (gestion du flood)\r
+                  Delta = delta_date_ms(Auteur#user.date_derniere_connexion, now()),\r
+                  Nouvel_indice_flood = Auteur#user.indice_flood + if Delta =< ?DUREE_SPAM -> 2; true -> -1 end,\r
+                  Auteur_maj = Auteur#user{\r
+                     indice_flood = if Nouvel_indice_flood > ?INDICE_SPAM_MAX -> ?INDICE_SPAM_MAX; Nouvel_indice_flood < 0 -> 0; true -> Nouvel_indice_flood end,\r
+                     date_derniere_connexion = now()\r
+                  },\r
+                  % est-ce que l'auteur à trop floodé ?\r
+                  if Auteur#user.indice_flood =/= ?INDICE_SPAM_MAX, Auteur_maj#user.indice_flood =:= ?INDICE_SPAM_MAX, Delta =< ?DUREE_BLOCAGE_SPAM ->\r
+                     mnesia:write(Auteur#user{indice_flood = Auteur_maj#user.indice_flood}),\r
+                     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.");\r
+                  Auteur#user.indice_flood =:= ?INDICE_SPAM_MAX, Delta =< ?DUREE_BLOCAGE_SPAM ->\r
+                     {erreur, "Bloqué pour cause de flood"};\r
+                  true ->     \r
+                     mnesia:write(Auteur_maj),\r
+                     Id = nouvel_id(minichat),\r
+                     inserer_reponses(Id, Repond_A_ids),\r
+                     mnesia:write(#minichat{\r
+                        id = Id,\r
+                        auteur_id = Auteur#user.id,\r
+                        date = now(),\r
+                        pseudo = Auteur#user.pseudo,\r
+                        contenu = Mess,\r
+                        racine_id = if Racine_id =:= undefined -> Id; true -> Racine_id end\r
+                     }),\r
+                     Id\r
+                  end\r
+            end;\r
+         _ ->\r
+            {erreur, "L'auteur du message est introuvable"}\r
+      end\r
+   end,\r
+   resultat_transaction(mnesia:transaction(F)).\r
+   \r
+% Définit Id_repondant comme étant la réponse à Ids. Ids est une liste d'id.\r
+inserer_reponses(Id_repondant, [Id_mess | Reste]) ->\r
+   mnesia:write(#reponse_minichat{repondant = Id_repondant, cible = Id_mess}),\r
+   inserer_reponses(Id_repondant, Reste);\r
+inserer_reponses(_, []) ->\r
+   ok.\r
+   \r
\r
+% Permet de créer un message système.\r
+% Renvoie l'id du message système\r
+nouveau_message_sys(Mess) ->\r
+   nouveau_message_sys(Mess, undefined).\r
+   \r
+\r
+% Création d'un message système lié à un troll.\r
+nouveau_message_sys(Mess, Troll_id) ->\r
+   {ok, Root} = user_by_id(0),\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         Id = nouvel_id(minichat),\r
+         mnesia:write(#minichat{id=Id, auteur_id=0, date=now(), pseudo=Root#user.pseudo, contenu=Mess, troll_id=Troll_id, racine_id=Id}),\r
+         Id\r
+      end\r
+   )).\r
+   \r
+   \r
+% Renvoie N messages se trouvant sur la première page\r
+messages(N) ->  \r
+   messages(N, 1).\r
+\r
+\r
+% Renvoie N messages se trouvant sur la page P\r
+messages(N, P) ->\r
+   F = fun() ->\r
+      C = cursor(\r
+         qlc:keysort(\r
+            #minichat.id, \r
+            q([E#minichat{contenu = contenu_message(E)} || E <- mnesia:table(minichat)]),\r
+            [{order, descending}]\r
+         ),\r
+         [{tmpdir, ?KEY_SORT_TEMP_DIR}]\r
+      ),\r
+      if P > 1 -> qlc:next_answers(C, N * (P - 1));\r
+         true -> ok\r
+      end,\r
+      R = qlc:next_answers(C, N),\r
+      qlc:delete_cursor(C),\r
+      R\r
+   end,\r
+   lists:reverse(resultat_transaction(mnesia:transaction(F))).\r
+\r
+\r
+% Renvoie les messages manquants pour la page P en sachant qu'il y a N message\r
+% par page et que le dernier message que l'on possède est Id\r
+messages(Id, N, P) ->\r
+   lists:filter(fun (M) -> M#minichat.id > Id end, messages(N, P)).\r
+   \r
+   \r
+% Renvoie {ok, #minichat} (voir #minichat de euphorik_bd.hrl) à partir de son id.\r
+message_by_id(Id) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         case mnesia:read({minichat, Id}) of\r
+            [] -> erreur;\r
+            [M] ->\r
+               {ok, M#minichat{contenu = contenu_message(M)}}\r
+         end\r
+      end\r
+   )).\r
+   \r
+   \r
+% Renvoie le contenu d'un message donnée, à utiliser à l'intérieur d'une transaction.\r
+% TODO : Cette fonction pourrait être remplacé par un "outer-join", est-ce possible avec qlc ?\r
+contenu_message(E) ->\r
+   case mnesia:read({troll, E#minichat.troll_id}) of\r
+      [] -> E#minichat.contenu;\r
+      [T] -> E#minichat.contenu ++ T#troll.content\r
+   end.\r
+  \r
+\r
+% Renvoie une liste de message (voir #minichat de euphorik_bd.hrl) à partir d'une liste d'id (Ids).\r
+% TODO : optimisations ? serait-ce du O(n) ?\r
+% Bon de toutes façons on s'en fout c'est pas utilisé :)\r
+messages_by_ids(Ids) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         e(qlc:keysort(\r
+            #minichat.id,\r
+            q([E || E <- mnesia:table(minichat), lists:any(fun(Id) -> Id =:= E#minichat.id end, Ids)]),\r
+            [{order, ascending}]\r
+         ),[{tmpdir, ?KEY_SORT_TEMP_DIR}])\r
+      end\r
+   )).\r
+   \r
+\r
+% Est-ce qu'un message existe ? Renvoie un boolean.\r
+% TODO : ya pas plus simple ?\r
+message_existe(Id) ->\r
+   resultat_transaction(mnesia:transaction(fun() ->\r
+      length(e(q([E#minichat.id || E <- mnesia:table(minichat), E#minichat.id =:= Id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}])) =:= 1\r
+   end)).\r
+   \r
+   \r
+% Renvoie les reponses (utilisé normalement uniquement pendant le debug).\r
+reponses() ->\r
+   F = fun() ->\r
+      e(q([E || E <- mnesia:table(reponse_minichat)]), [{tmpdir, ?KEY_SORT_TEMP_DIR}])\r
+   end,\r
+   resultat_transaction(mnesia:transaction(F)).\r
+   \r
+   \r
+% Renvoie les messages auquel M_id répond.\r
+parents(M_id) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         e(q(\r
+            [M || R <- mnesia:table(reponse_minichat),\r
+            M <- mnesia:table(minichat),\r
+            R#reponse_minichat.repondant =:= M_id,\r
+            M#minichat.id =:= R#reponse_minichat.cible]), [{tmpdir, ?KEY_SORT_TEMP_DIR}])\r
+      end\r
+   )).\r
+   \r
+   \r
+% Renvoie les message qui repondent à M_id\r
+enfants(M_id) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         e(q(\r
+            [M || R <- mnesia:table(reponse_minichat),\r
+            M <- mnesia:table(minichat),\r
+            R#reponse_minichat.cible =:= M_id,\r
+            M#minichat.id =:= R#reponse_minichat.repondant]), [{tmpdir, ?KEY_SORT_TEMP_DIR}])\r
+      end\r
+   )).\r
+   \r
+\r
+% Est-ce que le message Id_mess est une réponse d'une message de Id_user ?\r
+est_une_reponse_a_user(Id_user, Id_mess) ->\r
+   case mnesia:transaction(\r
+            fun() ->\r
+               e(q([\r
+                  M#minichat.auteur_id || M <- mnesia:table(minichat), R <- mnesia:table(reponse_minichat),\r
+                  M#minichat.auteur_id =:= Id_user, M#minichat.id =:= R#reponse_minichat.cible, R#reponse_minichat.repondant =:= Id_mess\r
+               ]), [{unique_all, true}, {tmpdir, ?KEY_SORT_TEMP_DIR}])\r
+            end\r
+         ) of\r
+      {atomic, [_]} -> true;\r
+      _ -> false\r
+   end.\r
+\r
+   \r
+% Est-ce que Id_user à répondu au message Id_mess\r
+a_repondu_a_message(Id_user, Id_mess) ->\r
+   case mnesia:transaction(\r
+            fun() ->\r
+               e(q([\r
+                  M#minichat.auteur_id || M <- mnesia:table(minichat), R <- mnesia:table(reponse_minichat),\r
+                  R#reponse_minichat.cible =:= Id_mess, R#reponse_minichat.repondant =:= M#minichat.id, M#minichat.auteur_id =:= Id_user\r
+               ]), [{unique_all, true}, {tmpdir, ?KEY_SORT_TEMP_DIR}])\r
+            end\r
+         ) of\r
+      {atomic, [_]} -> true;\r
+      _ -> false\r
+   end.\r
+   \r
+   \r
+% Est-ce que Id_user possède Id_mess ?\r
+possede_message(Id_user, Id_mess) ->\r
+   case mnesia:transaction(\r
+            fun() ->\r
+               e(q([E#minichat.auteur_id || E <- mnesia:table(minichat), E#minichat.id =:= Id_mess]), [{tmpdir, ?KEY_SORT_TEMP_DIR}])\r
+            end\r
+         ) of\r
+      {atomic, [Id_user | []]} -> true;\r
+      _ -> false\r
+   end.\r
+   \r
+   \r
+% renvoie la liste des ip bannies\r
+% liste de {ip, temps_restant(en minutes), Users} ou Users est une liste de {pseudo, login}\r
+% TODO : déterminer la complexité de cette fonction. (Il n'y a pas d'index sur #user.last_ip)\r
+list_ban() ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         Now = now(),\r
+         e(qlc:keysort(1, q([\r
+            {\r
+               IP#ip_table.ip,\r
+               delta_date_minute(date_plus_minutes(IP#ip_table.ban, IP#ip_table.ban_duration), Now),\r
+               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}])\r
+            } ||\r
+            IP <- mnesia:table(ip_table),\r
+            if IP#ip_table.ban =:= undefined -> false; true -> date_plus_minutes(IP#ip_table.ban, IP#ip_table.ban_duration) > Now end\r
+         ])), [{tmpdir, ?KEY_SORT_TEMP_DIR}])\r
+      end\r
+   )).\r
+\r
+\r
+% Bannie une ip pour un certain temps (en minute).\r
+ban(IP, Duration) ->\r
+   mnesia:transaction(\r
+      fun() ->\r
+         case mnesia:wread({ip_table, IP}) of\r
+            [IP_tuple] ->\r
+               mnesia:write(IP_tuple#ip_table{ban = now(), ban_duration = Duration});\r
+            _ ->\r
+               mnesia:write(#ip_table{ip = IP, ban = now(), ban_duration = Duration})\r
+          end\r
+      end\r
+   ).\r
+   \r
+\r
+% Débanni une ip\r
+deban(IP) ->\r
+   ban(IP, 0).\r
+\r
+   \r
+% Renvoie soit {true, Temps} où Temps est le temps en minutes pendant lequel le user est encore banni\r
+% ou false.\r
+est_banni(User_id) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         case e(q([\r
+            {IP#ip_table.ban, IP#ip_table.ban_duration} ||\r
+            U <- mnesia:table(user),\r
+            U#user.id =:= User_id,\r
+            IP <- mnesia:table(ip_table),\r
+            IP#ip_table.ip =:= U#user.last_ip\r
+         ]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of\r
+            [{Ban, Ban_duration}] ->\r
+               Echeance = date_plus_minutes(Ban, Ban_duration),\r
+               Now = now(),\r
+               if Echeance < Now -> % l'échéance est passée\r
+                     false;\r
+                  true ->\r
+                     {true, delta_date_minute(Echeance, Now)}\r
+               end;\r
+            _ ->\r
+               false\r
+         end\r
+      end\r
+   )).\r
+   \r
+   \r
+% Ban est une date tel que retourner par now().\r
+% Ban_duration est un temps en minutes.\r
+% retourne une date.\r
+date_plus_minutes(Ban, Ban_duration) ->\r
+   Duration_sec = Ban_duration * 60,\r
+   {MegaSec, Sec, MicroSec} = Ban,\r
+   {MegaSec + if Sec + Duration_sec >= 1000000 -> 1; true -> 0 end,(Sec + Duration_sec) rem 1000000, MicroSec}.\r
+   \r
+\r
+% Si deux enregistrements consequtifs de la même IP sont fait en moins d'une seconde alors \r
+% ip_table.nb_try_register est incrémenté de 1 sinon il est décrémenté de 1 (jusqu'a 0).\r
+% Si ip_table.nb_try_register vaut 5 alors l'ip ne peux plus s'enregistrer pour une heure.\r
+can_register(IP) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         case e(q([I || I <- mnesia:table(ip_table), I#ip_table.ip =:= IP]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of\r
+            [] -> \r
+               mnesia:write(#ip_table{ip = IP, date_last_try_register = now()}),\r
+               true;\r
+            [T] ->\r
+               Delta = delta_date_ms(T#ip_table.date_last_try_register, now()),\r
+               if T#ip_table.nb_try_register =:= ?NB_MAX_FLOOD_REGISTER, Delta < ?TEMPS_BAN_FLOOD_REGISTER ->\r
+                     false;\r
+                  true ->\r
+                     mnesia:write(#ip_table{\r
+                        ip = IP,\r
+                        date_last_try_register = now(),\r
+                        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\r
+                     }),\r
+                     true\r
+               end\r
+         end\r
+      end\r
    )).\r
    \r
-   
-% Ajoute un message. Repond_A est une liste d'id auquel le message répond
-% retourne soit l'id du message soit {erreur, <raison>}.
-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.
-   
-   \r
-% 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) ->
+\r
+% Renvoie tous les trolls\r
+trolls() ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         e(qlc:keysort(#troll.id, q([T || T <- mnesia:table(troll)])), [{tmpdir, ?KEY_SORT_TEMP_DIR}])\r
+      end\r
+   )).\r
+   \r
+   \r
+% Renvoie les trolls manquants posté après Last_id.\r
+trolls(Last_id) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         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}])\r
+      end\r
+   )).\r
+   \r
\r
+ % Crée un nouveau troll.\r
+ % Renvoie l'id du nouveau troll\r
+ % ou max_troll_reached_per_user si le nombre de troll posté par l'utilisateur max a été atteind\r
+ % ou max_troll_reached si le nombre de troll posté max a été atteind\r
+ % ou user_unknown\r
+put_troll(User_id, Content) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         % control le nombre de troll déjà posté\r
+         Nb_troll_poste_par_user = length(e(q(\r
+            [\r
+               E#troll.id || E <- mnesia:table(troll),\r
+               E#troll.id_user =:= User_id,\r
+               E#troll.date_post =:= undefined\r
+            ]\r
+         ), [{tmpdir, ?KEY_SORT_TEMP_DIR}])),\r
+         Nb_troll_poste_total = length(e(q(\r
+            [\r
+               E#troll.id || E <- mnesia:table(troll),\r
+               E#troll.date_post =:= undefined\r
+            ]\r
+         ), [{tmpdir, ?KEY_SORT_TEMP_DIR}])),\r
+         User = user_by_id(User_id),\r
+         case User of\r
+            {ok, _} ->\r
+               if Nb_troll_poste_par_user >= ?NB_MAX_TROLL_WAITING_BY_USER ->\r
+                     max_troll_reached_per_user;\r
+                  Nb_troll_poste_total >= ?NB_MAX_TROLL_WAITING ->\r
+                     max_troll_reached;\r
+                  true ->\r
+                     Id = nouvel_id(minichat),\r
+                     mnesia:write(#troll{id = Id, id_user = User_id, date_create = now(), content = Content}),\r
+                     Id\r
+               end;\r
+            _ ->\r
+               user_unknown\r
+         end\r
+      end\r
+   )).\r
+\r
+\r
+% renvoie ok | erreur\r
+mod_troll(Troll_id, Content) ->\r
+   mnesia:transaction(\r
+      fun() ->\r
+         case mnesia:wread({troll, Troll_id}) of\r
+            [Troll = #troll{date_post = undefined}] ->\r
+               mnesia:write(Troll#troll{content = Content});\r
+            _ ->\r
+               mnesia:abort("mod_troll: Troll inconnu ou déjà posté")\r
+          end\r
+      end\r
+   ).\r
+   \r
+   \r
+del_troll(Troll_id) ->\r
+   mnesia:transaction(\r
+      fun() ->\r
+         case mnesia:wread({troll, Troll_id}) of\r
+            [#troll{date_post = undefined}] ->\r
+               mnesia:delete({troll, Troll_id});\r
+            _ ->\r
+               mnesia:abort("mod_troll: Troll inconnu ou déjà posté")\r
+          end\r
+      end\r
+   ).\r
\r
\r
+troll_by_id(Troll_id) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         case e(q([T || T <- mnesia:table(troll), T#troll.id =:= Troll_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of\r
+            [T] -> {ok, T};\r
+            _ ->\r
+               erreur\r
+         end\r
+      end\r
+   )).\r
+   \r
+\r
+% Renvoie le troll actuel qui se trouve sur la page principale.\r
+% Renvois aucun si pas de troll courant.\r
+current_troll() ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         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
+         R = case qlc:next_answers(C, 1) of\r
+            [T] -> T;\r
+            _ -> aucun\r
+         end,\r
+         qlc:delete_cursor(C),\r
+         R\r
+      end\r
+   )).\r
+\r
+\r
+% Elit un troll au hasard parmis les trolls en attente (leur date_post =:= undefined)\r
+% Un message est posté par 'Sys' et le troll elu est lié à ce message\r
+% met à jour sa date de post.\r
+% renvoie plus_de_trolls si il n'y a aucun troll en attente.\r
+elire_troll() ->       \r
+   {A1,A2,A3} = now(),\r
+   random:seed(A1, A2, A3),\r
+   mnesia:transaction(\r
+      fun() ->\r
+         case e(q([T || T <- mnesia:table(troll), T#troll.date_post =:= undefined]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of\r
+            [] ->\r
+               plus_de_trolls;\r
+            Trolls ->\r
+               Troll = lists:nth(random:uniform(length(Trolls)), Trolls),\r
+               Troll2 = Troll#troll{date_post = now()},\r
+               mnesia:write(Troll2),\r
+               nouveau_message_sys("Troll de la semaine : ", Troll2#troll.id)\r
+         end\r
+      end\r
+   ).\r
+   \r
+\r
+% Renvoie l'id du message associé au troll dont l'id est donnée.\r
+% Renvoie undefined si il n'y en a pas.\r
+message_id_associe(Troll_id) ->\r
+   resultat_transaction(mnesia:transaction(\r
+      fun() ->\r
+         case e(q([M#minichat.id || M <- mnesia:table(minichat), M#minichat.troll_id =:= Troll_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of\r
+            [Id] -> Id;\r
+            _ -> undefined\r
+         end\r
+      end\r
+   )).\r
+\r
+   \r
+% Renvoie le résultat d'une transaction (en décomposant le tuple fournit)\r
+resultat_transaction({_, T}) ->\r
+   T.\r
+   \r
+\r
+% Retourne la difference entre deux timestamp (erlang:now()) en miliseconde\r
+delta_date_ms(D1, D2) ->\r
+   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).\r
+\r
+% Retourne la différence entre deux timestamp (erlang:now) en minutes\r
+delta_date_minute(D1, D2) ->\r
+   trunc(delta_date_ms(D1, D2) / 1000 / 60).\r
+\r
+\r
+% Renvoie un nouvel id pour une table donnée\r
+nouvel_id(Table) ->\r
    mnesia:dirty_update_counter(counter, Table, 1).\r
    
\ No newline at end of file
index ad5c04c..b779583 100755 (executable)
@@ -1,84 +1,84 @@
-% Copyright 2008 Grégory Burri\r
-%\r
-% This file is part of Euphorik.\r
-%\r
-% Euphorik is free software: you can redistribute it and/or modify\r
-% it under the terms of the GNU General Public License as published by\r
-% the Free Software Foundation, either version 3 of the License, or\r
-% (at your option) any later version.\r
-%\r
-% Euphorik is distributed in the hope that it will be useful,\r
-% but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-% GNU General Public License for more details.\r
-%\r
-% You should have received a copy of the GNU General Public License\r
-% along with Euphorik.  If not, see <http://www.gnu.org/licenses/>.\r
-%\r
-% @author GBurri\r
+% 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 <http://www.gnu.org/licenses/>.
+%
+% @author GBurri
 
 
 % Version de la BD
 -define(VERSION_BD, 2).
 
 
-% Pour générer des id\r
--record(counter,\r
-   {\r
-      key,\r
-      value\r
-   }).\r
+% 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
-   }).\r
+   }).
 
-\r
-% décrit un enregistrement d'un message\r
+
+% décrit un enregistrement d'un message
 -record(minichat,
-   {\r
-      id, % integer\r
+   {
+      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
-   }).\r
+      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
+   }).
    
-   \r
-% type bag\r
-% 'repondant' repond à 'cible'\r
--record(reponse_minichat,\r
-   {\r
-      repondant, % -> #minichat.id\r
-      cible % -> #minichat.id\r
-   }). \r
+   
+% type bag
+% 'repondant' repond à 'cible'
+-record(reponse_minichat,
+   {
+      repondant, % -> #minichat.id
+      cible % -> #minichat.id
+   }). 
+
 
-\r
--record(user,\r
-   {\r
-      id,\r
-      cookie, % string()\r
+-record(user,
+   {
+      id,
+      cookie, % string()
       pseudo = [], % string()
       login = [], % string()
       password = [], % string() (md5)
-      email = [], % string()\r
-      date_creation, % erlang:now()\r
-      date_derniere_connexion, % erlang:now(), est mis à jour lors de n'importe quelle activitée (envoie de message par exemple)\r
+      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\r
+      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é
    }).
    
    
       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
index c0791b5..bf6ec5b 100755 (executable)
@@ -1,51 +1,51 @@
-% Copyright 2008 Grégory Burri\r
-%\r
-% This file is part of Euphorik.\r
-%\r
-% Euphorik is free software: you can redistribute it and/or modify\r
-% it under the terms of the GNU General Public License as published by\r
-% the Free Software Foundation, either version 3 of the License, or\r
-% (at your option) any later version.\r
-%\r
-% Euphorik is distributed in the hope that it will be useful,\r
-% but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-% GNU General Public License for more details.\r
-%\r
-% You should have received a copy of the GNU General Public License\r
-% along with Euphorik.  If not, see <http://www.gnu.org/licenses/>.\r
-
-\r
-% Le temps d'attente après une erreur de login (mauvais login/pass) : une demie seconde\r
-% Permet d'éviter (limiter) les attaques par dictionnaire\r
+% 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 <http://www.gnu.org/licenses/>.
+
+
+% 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 (file)
index 0000000..e69de29
diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties
new file mode 100644 (file)
index 0000000..6691e30
--- /dev/null
@@ -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 (file)
index 0000000..c1f155a
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project-private xmlns="http://www.netbeans.org/ns/project-private/1">
+    <editor-bookmarks xmlns="http://www.netbeans.org/ns/editor-bookmarks/1"/>
+</project-private>
diff --git a/nbproject/project.properties b/nbproject/project.properties
new file mode 100644 (file)
index 0000000..93e29d0
--- /dev/null
@@ -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 (file)
index 0000000..fa0f3ba
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.erlang.project</type>
+    <configuration>
+        <data xmlns="http://www.netbeans.org/ns/erlang-project/1">
+            <name>euphorik</name>
+            <source-roots>
+                <root id="src.css.dir"/>
+                <root id="src.doc.dir"/>
+                <root id="src.pages.dir"/>
+                <root id="src.tools.dir"/>
+                <root id="src.js.dir"/>
+                <root id="src.dir"/>
+                <root id="include.dir"/>
+            </source-roots>
+            <include-roots/>
+            <test-roots/>
+        </data>
+    </configuration>
+</project>