ADD script permettant la mise à jour de la BD et le rechargement des modules lors...
[euphorik.git] / modules / erl / euphorik_bd.erl
index be43e59..187d6ea 100755 (executable)
@@ -1,8 +1,26 @@
-% coding: utf-8
+% coding: utf-8\r
+% 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
+% 
 % Ce module permet de gérer les données persistantes lié au site d'euphorik.ch.
 % Il permet d'ajouter des message, de demande les messages sur une page donnée, etc..
 % Ce module utilise une base mnesia.
-% @author G.Burri
+% @author G.Burri\r
+
 
 -module(euphorik_bd).\r
 -export([
    connect/0,
    connect/1,
    reset/0,
+   update/0,
    
    % users :
    nouveau_user/2,
    nouveau_user/3,
-   set_profile/9,
+   set_profile/11,
    update_date_derniere_connexion/1,
    update_ip/2,
    update_pseudo_user/2,
    user_by_login_password/2,
    user_by_mess/1,
    toggle_ek_master/1,
+   css_from_user_cookie/1,
    
    % messages :
    nouveau_message/3,
    nouveau_message_sys/1,
+   nouveau_message_sys/2,
    messages/1,
    messages/2,\r
    messages/3,
    % trolls :
    trolls/0,
    trolls/1,
-   trolls_attente/1,
    put_troll/2,
    mod_troll/2,
    del_troll/1,
    troll_by_id/1,
    current_troll/0,
    elire_troll/0,
+   message_id_associe/1,
    
    % versions :
    update_version/1,
@@ -91,9 +112,13 @@ create_tables() ->
       {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]},
+      {index, [auteur_id, troll_id]},
       {disc_copies, [node()]}
    ]),
    mnesia:create_table(reponse_minichat, [
@@ -135,13 +160,45 @@ reset() ->
    mnesia:clear_table(troll),
    mnesia:clear_table(ip_table),\r
    % crée l'utilisateur root\r
-   mnesia:transaction(fun() ->\r
+   mnesia:transaction(fun() ->
+      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).
 
 
+% Met à jour la bd, compare ?VERSION_BD avec la version dans la table 'proprietes'
+% et exécute les patchs nécessaires.
+update() ->
+   mnesia:transaction(
+      fun() ->
+         case mnesia:read({proprietes, version}) of
+            [#proprietes{nom = Version}] ->
+               update(Version);
+            _ ->
+               erreur
+         end
+      end
+   ).
+   
+
+% Mise à jour de la BD.
+% attention : il est nécessaire de se trouver dans une transaction.
+update(?VERSION_BD) -> fini;
+update(Version) ->
+   patch(Version),
+   update(Version + 1).
+   
+   
+% Applique une modification de la BD pour passer d'une version à la suivante.
+% 1 -> 2
+patch(1) ->
+   ok.
+% 2 -> 3
+%patch(2) ->
+
+
 % Ajoute un nouveau user et le renvoie
 nouveau_user(Pseudo, Cookie) ->
    F = fun() ->
@@ -165,12 +222,12 @@ nouveau_user(Login, Password, Cookie) ->
 
 
 % 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, Page_principale, Conversations) ->
+set_profile(Cookie, Login, Password, Pseudo, Email, Css, Nick_format, View_times, View_tooltips, Page_principale, 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} ->               
+                  {ok, User} ->
                      case user_by_login(Login) of
                         {ok, U} when Login =/= [], U#user.id =/= User#user.id ->
                            login_deja_pris;
@@ -184,6 +241,8 @@ set_profile(Cookie, Login, Password, Pseudo, Email, Css, Nick_format, Page_princ
                               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,
                               page_principale = if is_integer(Page_principale), Page_principale > 0 -> Page_principale; true -> User#user.page_principale end,
                               conversations = if is_list(Conversations) -> Conversations; true -> User#user.conversations end
                            },
@@ -340,7 +399,19 @@ toggle_ek_master(User_id) ->
       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() ->
@@ -388,7 +459,7 @@ nouveau_message(Mess, Auteur_id, Repond_A) ->
             % 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.");
+               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 ->     
@@ -413,13 +484,19 @@ inserer_reponses(_, []) ->
 % 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),
-   mnesia:transaction(
+   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})
+         mnesia:write(#minichat{id=Id, auteur_id=0, date=now(), pseudo=Root#user.pseudo, contenu=Mess, troll_id=Troll_id}),
+         Id
       end
-   ).
+   )).
    
    
 % Renvoie N messages se trouvant sur la première page
@@ -430,7 +507,10 @@ messages(N) ->
 % Renvoie N messages se trouvant sur la page P
 messages(N, P) ->  
    F = fun() ->
-      C = cursor(q([E || E <- qlc:keysort(2, mnesia:table(minichat), [{order, descending}])])),
+      C = cursor(q([
+         E#minichat{contenu = contenu_message(E)} ||
+         E <- qlc:keysort(2, mnesia:table(minichat), [{order, descending}])
+      ])),
       if P > 1 -> qlc:next_answers(C, N * (P - 1));
          true -> ok
       end,
@@ -451,12 +531,18 @@ messages(Id, N, P) ->
 message_by_id(Id) ->
    case resultat_transaction(mnesia:transaction(
       fun() ->
-         e(q([E || E <- qlc:keysort(2, mnesia:table(minichat), [{order, ascending}]), Id =:= E#minichat.id]))
+         e(q([E#minichat{contenu = contenu_message(E)} || E <- qlc:keysort(2, mnesia:table(minichat), [{order, ascending}]), Id =:= E#minichat.id]))
       end
    )) of
       [M] -> {ok, M};
       _ -> erreur
    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) ->
+   lists:flatten(E#minichat.contenu ++ e(q([T#troll.content || T <- mnesia:table(troll), T#troll.id =:= E#minichat.troll_id]))).
   
 
 % Renvoie une liste de message (voir #minichat de euphorik_bd.hrl) à partir d'une liste d'id (Ids).
@@ -655,59 +741,13 @@ trolls() ->
 % Renvoie les trolls manquants posté après Last_id.
 trolls(Last_id) ->
    resultat_transaction(mnesia:transaction(
-      e(q([T || T <- qlc:keysort(2, mnesia:table(troll)), T#troll.id > Last_id, T#troll.date_post =:= undefined]))
+      fun() ->
+         e(q([T || T <- qlc:keysort(2, mnesia:table(troll)), T#troll.id > Last_id, T#troll.date_post =:= undefined]))
+      end
    )).
    
-   
-% Renvoie les trolls manquants posté après Last_id.
-% Si pas de trolls alors attend un événement tel qu'un ajout, une modification ou une suppression.
-% renvoie :
-%  {mod, Troll}
-% ou {add, [Trolls]}
-% ou {del, Troll_id}
-% ou timeout
-trolls_attente(Last_id) ->
-   case mnesia:subscribe({table, troll, detailed}) of
-      {error, E} = E ->
-         E;
-      _ ->
-         R = case resultat_transaction(mnesia:transaction(
-               fun() ->
-                  e(q([T || T <- qlc:keysort(2, mnesia:table(troll)), T#troll.id > Last_id, T#troll.date_post =:= undefined]))
-               end
-            )) of
-               [] -> % pas de trolls
-                  attend_evenement_troll();
-               Trolls ->
-                  {add, Trolls}
-         end,
-         mnesia:unsubscribe({table, troll, detailed}),
-         R
-   end.
-   
-attend_evenement_troll() ->
-   % s'il n'y a pas de trolls que l'utilisateur n'a pas connaissance alors on attend un événement
-   receive
-      % cas où un troll est choisit comme courant
-      {mnesia_table_event, {write, troll, Troll, [Old_troll | _], _}} when Old_troll#troll.date_post =:= undefined, Troll#troll.date_post =/= undefined ->
-         {del, Troll#troll.id};
-      {mnesia_table_event, {write, troll, Troll, [_Old_troll | _], _}} ->
-         {mod, Troll};
-      {mnesia_table_event, {write, troll, Troll, [], _}} ->
-         {add, [Troll]};
-      {mnesia_table_event, {delete, troll, {troll, Id}, _, _}} ->
-         {del, Id};
-      {tcp_closed, _} ->
-         exit(normal);
-      _ ->
-         attend_evenement_troll()
-   % 60 minutes de timeout (on ne sais jamais)
-   % Après 60 minutes de connexion, le client doit donc reétablir une connexion
-   after 1000 * 60 * 60 -> 
-      timeout
-   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
@@ -805,6 +845,7 @@ current_troll() ->
 
 
 % 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() ->       
@@ -818,12 +859,25 @@ elire_troll() ->
             Trolls ->
                Troll = lists:nth(random:uniform(length(Trolls)), Trolls),
                Troll2 = Troll#troll{date_post = now()},
-               mnesia:write(Troll2)
+               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])) of
+            [Id] -> Id;
+            _ -> undefined
+         end
+      end
+   )).
+      
    
 update_version(1) ->
    mnesia:transform_table(