X-Git-Url: http://git.euphorik.ch/?p=euphorik.git;a=blobdiff_plain;f=modules%2Ferl%2Feuphorik_bd.erl;h=81a36a0d9ceb60e7de4c132b247d807e9d861f72;hp=904fc0a42bb154e2c61c472b746f690ec929c38e;hb=6e96af2afdc875932c19685110c97ea7837944c6;hpb=d1e6f8e7c86f1ee75e854266463541ffedb916c9 diff --git a/modules/erl/euphorik_bd.erl b/modules/erl/euphorik_bd.erl index 904fc0a..81a36a0 100755 --- a/modules/erl/euphorik_bd.erl +++ b/modules/erl/euphorik_bd.erl @@ -24,6 +24,10 @@ -module(euphorik_bd). -export([ + % texte : + get_texte/1, + get_texte/2, + % users : nouveau_user/2, nouveau_user/4, @@ -31,19 +35,15 @@ 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, is_ek_master_from_cookie/1, - % messages :e + % messages : nouveau_message/3, nouveau_message_sys/1, nouveau_message_sys/2, @@ -89,6 +89,20 @@ -include_lib("stdlib/include/qlc.hrl"). +get_texte(Id) -> + get_texte(Id, fr). + + +% TODO : généraliser la langue +get_texte(Id, _Lang = fr) -> + resultat_transaction(mnesia:transaction(fun() -> + case mnesia:read({texte, Id}) of + [#texte{fr = Texte}] -> Texte; + _ -> "Message " ++ integer_to_list(Id) ++ " unknown" + end + end)). + + % Ajoute un nouveau user et le renvoie nouveau_user(Cookie, Profile) -> F = fun() -> @@ -104,7 +118,7 @@ nouveau_user(Cookie, Profile) -> nouveau_user(Login, Password, Cookie, Profile) -> F = fun() -> Id = nouvel_id(user), - User = #user{id = Id, cookie = Cookie, login = Login, password = Password, date_creation = now(), date_derniere_connexion = now(), profile = Profile#profile{pseudo = login}}, + User = #user{id = Id, cookie = Cookie, login = Login, password = Password, date_creation = now(), date_derniere_connexion = now(), profile = Profile#profile{pseudo = Login}}, mnesia:write(User), User end, @@ -176,83 +190,14 @@ update_pseudo_user(UserId, Pseudo) -> 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, profile = #profile{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 + case mnesia:index_read(user, Cookie, #user.cookie) of [User] -> {ok, User}; _ -> erreur end @@ -263,7 +208,11 @@ user_by_cookie(Cookie) -> 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 +% case e(q([E || E <- mnesia:table(user), E#user.id =:= ID]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) of +% [User] -> {ok, User}; +% _ -> erreur +% end + case mnesia:read({user, ID}) of [User] -> {ok, User}; _ -> erreur end @@ -282,19 +231,6 @@ user_by_login(Login) -> end )). - -toggle_ek_master(User_id) -> - resultat_transaction(mnesia:transaction( - fun() -> - Users = e(q([E || E <- mnesia:table(user), E#user.id =:= User_id]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]), - case Users of - [User] -> - mnesia:write(User#user{ek_master = not User#user.ek_master}); - _ -> erreur - end - end - )). - % Renvoie une chaine représentant le cookie ou undefined si pas trouvé. css_from_user_cookie(Cookie) -> @@ -340,14 +276,17 @@ user_by_mess(Id) -> % retourne soit l'id du message soit {erreur, }. nouveau_message(Mess, Auteur_id, Repond_A_ids) -> % regarde si les id 'Repond_A' existent - F = fun() -> - Repond_a = e( - q([M || M <- mnesia:table(minichat), lists:member(M#minichat.id, Repond_A_ids)]), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ), + F = fun() -> + Repond_a = lists:map( + fun(Repond_a_id) -> + [M] = mnesia:wread({minichat, Repond_a_id}), + M + end, + Repond_A_ids + ), Racine_id = case Repond_a of [] -> undefined; - [M | _ ] -> + [M | _] -> Une_racine = M#minichat.racine_id, % vérification que tout les messages de Repond_a possède la même racine (même conversation) case lists:all(fun(R) -> R#minichat.racine_id =:= Une_racine end, Repond_a) of @@ -356,42 +295,43 @@ nouveau_message(Mess, Auteur_id, Repond_A_ids) -> _ -> {erreur, "Les messages ne font pas partie de la même conversation"} end - end, + end, case Racine_id of {erreur, E} -> {erreur, E}; _ -> % 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 + case mnesia:wread({user, Auteur_id}) of [#user{profile = Profile} = 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()), + Now = now(), + Delta = euphorik_common: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() + 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("''" ++ Profile#profile.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 = Profile#profile.pseudo, - contenu = Mess, - racine_id = if Racine_id =:= undefined -> Id; true -> Racine_id end - }), - Id + mnesia:write(Auteur#user{indice_flood = Auteur_maj#user.indice_flood}), + nouveau_message_sys("\"" ++ Profile#profile.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 = Profile#profile.pseudo, + contenu = Mess, + racine_id = if Racine_id =:= undefined -> Id; true -> Racine_id end + }), + Id end end; _ -> @@ -464,15 +404,14 @@ message_by_id(Id) -> resultat_transaction(mnesia:transaction( fun() -> case mnesia:read({minichat, Id}) of - [] -> erreur; - [M] -> - {ok, M#minichat{contenu = contenu_message(M)}} + [M] -> {ok, M#minichat{contenu = contenu_message(M)}}; + _ -> erreur end end )). -% Renvoie le contenu d'un message donnée, à utiliser à l'intérieur d'une transaction. +% Renvoie le contenu d'un message donnée en fonction du troll associé, à 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 @@ -538,64 +477,81 @@ enfants(M_id) -> )). -% Renvoie les parents d'un message M (les messages auquels répond M) +% Renvoie les id des parents d'un message M (les messages auquels répond M) % ordrés du plus petit au plus grand.. +% On évite d'utiliser qlc pour des raisons de performance % @spec parents_id(integer()) -> [integer()] -parents_id(M) -> +parents_id(M_id) -> resultat_transaction(mnesia:transaction(fun() -> - e( - qlc:sort( - q([E#reponse_minichat.cible || E <- mnesia:table(reponse_minichat), E#reponse_minichat.repondant =:= M]), - [{order, ascending}] - ), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ) + case mnesia:read({reponse_minichat, M_id}) of + Parents when is_list(Parents) -> + lists:sort(lists:map( + fun(#reponse_minichat{cible = Cible}) -> Cible end, + Parents + )); + _ -> [] + end end)). % Renvoie les id des enfants d'un message M (les messages qui répondent à M) % ordrés du plus petit au plus grand. % @spec enfants_id(integer()) -> [integer()] -enfants_id(M) -> +enfants_id(M_id) -> resultat_transaction(mnesia:transaction(fun() -> - e( - qlc:sort( - q([E#reponse_minichat.repondant || E <- mnesia:table(reponse_minichat), E#reponse_minichat.cible =:= M]), - [{order, ascending}] - ), - [{tmpdir, ?KEY_SORT_TEMP_DIR}] - ) + case mnesia:index_read(reponse_minichat, M_id, #reponse_minichat.cible) of + Enfants when is_list(Enfants) -> + lists:sort(lists:map( + fun(#reponse_minichat{repondant = Repondant}) -> Repondant end, + Enfants + )); + _ -> [] + end end)). % Est-ce que le message Id_mess est une réponse d'une message de Id_user ? +% On evite d'utiliser qlc (ce qui était fait avant) pour des raisons de performance. 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. + resultat_transaction(mnesia:transaction( + fun() -> + case mnesia:read({reponse_minichat, Id_mess}) of + [] -> false; + Cibles -> + lists:any( + fun(#reponse_minichat{cible = Cible}) -> + case mnesia:read({minichat, Cible}) of + [#minichat{auteur_id = Id_user}] -> true; + _ -> false + end + end, + Cibles + ) + end + end + )). % Est-ce que Id_user à répondu au message Id_mess +% On evite d'utiliser qlc (ce qui était fait avant) pour des raisons de performance. 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. + resultat_transaction(mnesia:transaction( + fun() -> + case mnesia:index_read(reponse_minichat, Id_mess, #reponse_minichat.cible) of + [] -> false; + Reponses -> + lists:any( + fun(#reponse_minichat{repondant = Reponse}) -> + case mnesia:read({minichat, Reponse}) of + [#minichat{auteur_id = Id_user}] -> true; + _ -> false + end + end, + Reponses + ) + end + end + )). % Est-ce que Id_user possède Id_mess ? @@ -620,7 +576,7 @@ list_ban() -> 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), + euphorik_common:delta_date_minute(date_plus_minutes(IP#ip_table.ban, IP#ip_table.ban_duration), Now), e(q([{Profile#profile.pseudo, U#user.login} || #user{profile = Profile} = U <- mnesia:table(user), U#user.last_ip =:= IP#ip_table.ip]), [{tmpdir, ?KEY_SORT_TEMP_DIR}]) } || IP <- mnesia:table(ip_table), @@ -667,7 +623,7 @@ est_banni(User_id) -> if Echeance < Now -> % l'échéance est passée false; true -> - {true, delta_date_minute(Echeance, Now)} + {true, euphorik_common:delta_date_minute(Echeance, Now)} end; _ -> false @@ -696,11 +652,11 @@ can_register(IP) -> 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()), + Delta = euphorik_common: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{ + mnesia:write(T#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 @@ -864,15 +820,6 @@ message_id_associe(Troll_id) -> % 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