ADD licences sur les fichier .erl
[euphorik.git] / modules / erl / euphorik_bd.erl
1 % coding: utf-8
2 % Copyright 2008 Grégory Burri
3 %
4 % This file is part of Euphorik.
5 %
6 % Euphorik is free software: you can redistribute it and/or modify
7 % it under the terms of the GNU General Public License as published by
8 % the Free Software Foundation, either version 3 of the License, or
9 % (at your option) any later version.
10 %
11 % Euphorik is distributed in the hope that it will be useful,
12 % but WITHOUT ANY WARRANTY; without even the implied warranty of
13 % MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 % GNU General Public License for more details.
15 %
16 % You should have received a copy of the GNU General Public License
17 % along with Euphorik. If not, see <http://www.gnu.org/licenses/>.
18 %
19 % Ce module permet de gérer les données persistantes lié au site d'euphorik.ch.
20 % Il permet d'ajouter des message, de demande les messages sur une page donnée, etc..
21 % Ce module utilise une base mnesia.
22 % @author G.Burri
23
24
25 -module(euphorik_bd).
26 -export([
27 % gestion :
28 create/0,
29 connect/0,
30 connect/1,
31 reset/0,
32
33 % users :
34 nouveau_user/2,
35 nouveau_user/3,
36 set_profile/9,
37 update_date_derniere_connexion/1,
38 update_ip/2,
39 update_pseudo_user/2,
40 print_users/0,
41 print_users/1,
42 print_user/1,
43 user_by_cookie/1,
44 user_by_id/1,
45 user_by_login/1,
46 user_by_login_password/2,
47 user_by_mess/1,
48 toggle_ek_master/1,
49
50 % messages :
51 nouveau_message/3,
52 nouveau_message_sys/1,
53 messages/1,
54 messages/2,
55 messages/3,
56 message_by_id/1,
57 messages_by_ids/1,
58 message_existe/1,
59 reponses/0,
60 repond_a/1,
61 est_une_reponse_a_user/2,
62 a_repondu_a_message/2,
63 possede_message/2,
64
65 % ip :
66 list_ban/0,
67 ban/2,
68 deban/1,
69 est_banni/1,
70 can_register/1,
71
72 % trolls :
73 trolls/0,
74 trolls/1,
75 put_troll/2,
76 mod_troll/2,
77 del_troll/1,
78 troll_by_id/1,
79 current_troll/0,
80 elire_troll/0,
81
82 % versions :
83 update_version/1,
84
85 % utiles :
86 resultat_transaction/1
87 ]).
88 -import(qlc, [e/1, e/2, q/1, cursor/1]).
89 -include("../include/euphorik_bd.hrl").
90 -include("../include/euphorik_defines.hrl").
91 -include_lib("stdlib/include/qlc.hrl").
92
93
94 % Instructions pour créer une nouvelle base :
95 % $erl -sname yaws -mnesia dir '"/projets/euphorik/BD"'
96 % voir doc/installation.txt
97 % >l(euphorik_bd).
98 % >euphorik_bd:create().
99 create() ->
100 mnesia:stop(),
101 mnesia:delete_schema([node()]),
102 mnesia:create_schema([node()]), % nécessaire pour les tables sur disc
103 mnesia:start(),
104 create_tables(),
105 reset().
106 create_tables() ->
107 mnesia:create_table(counter, [
108 {attributes, record_info(fields, counter)},
109 {disc_copies, [node()]}
110 ]),
111 mnesia:create_table(minichat, [
112 {attributes, record_info(fields, minichat)},
113 {index, [auteur_id]},
114 {disc_copies, [node()]}
115 ]),
116 mnesia:create_table(reponse_minichat, [
117 {type, bag},
118 {attributes, record_info(fields, reponse_minichat)},
119 {index, [cible]},
120 {disc_copies, [node()]}
121 ]),
122 mnesia:create_table(user, [
123 {attributes, record_info(fields, user)},
124 {index, [cookie, login]},
125 {disc_copies, [node()]}
126 ]),
127 mnesia:create_table(ip_table, [
128 {attributes, record_info(fields, ip_table)},
129 {disc_copies, [node()]}
130 ]),
131 mnesia:create_table(troll, [
132 {attributes, record_info(fields, troll)},
133 {index, [date_post]},
134 {disc_copies, [node()]}
135 ]).
136
137
138 % Connexion à la base de données de yaws sur overnux
139 connect() ->
140 connect(yaws@flynux).
141 connect(Node) ->
142 mnesia:start(),
143 mnesia:change_config(extra_db_nodes, [Node]).
144
145
146 % Efface tous les users, minichat_reponse et minichat.
147 reset() ->
148 mnesia:clear_table(counter),
149 mnesia:clear_table(user),
150 mnesia:clear_table(reponse_minichat),
151 mnesia:clear_table(minichat),
152 mnesia:clear_table(troll),
153 mnesia:clear_table(ip_table),
154 % crée l'utilisateur root
155 mnesia:transaction(fun() ->
156 User = #user{id = 0, pseudo = "Sys", login = "Sys", date_creation = now(), date_derniere_connexion = now(), ek_master = true},
157 mnesia:write(User),
158 User
159 end).
160
161
162 % Ajoute un nouveau user et le renvoie
163 nouveau_user(Pseudo, Cookie) ->
164 F = fun() ->
165 Id = nouvel_id(user),
166 User = #user{id = Id, cookie = Cookie, pseudo = Pseudo, date_creation = now(), date_derniere_connexion = now()},
167 mnesia:write(User),
168 User
169 end,
170 resultat_transaction(mnesia:transaction(F)).
171
172
173 % Ajoute un nouveau user et le renvoie
174 nouveau_user(Login, Password, Cookie) ->
175 F = fun() ->
176 Id = nouvel_id(user),
177 User = #user{id = Id, cookie = Cookie, pseudo = Login, login = Login, password = Password, date_creation = now(), date_derniere_connexion = now()},
178 mnesia:write(User),
179 User
180 end,
181 resultat_transaction(mnesia:transaction(F)).
182
183
184 % Mise à par Cookie les autres peuvent être undefined ce qui veut dire qu'ils ne seront pas modifié.
185 set_profile(Cookie, Login, Password, Pseudo, Email, Css, Nick_format, Page_principale, Conversations) ->
186 if Nick_format =:= nick; Nick_format =:= login; Nick_format =:= nick_login ->
187 resultat_transaction(mnesia:transaction(
188 fun() ->
189 case user_by_cookie(Cookie) of
190 {ok, User} ->
191 case user_by_login(Login) of
192 {ok, U} when Login =/= [], U#user.id =/= User#user.id ->
193 login_deja_pris;
194 _ ->
195 User_modifie = User#user{
196 % TODO : pourquoi ne pas tester avec la valeur "undefined" plutôt qu'avec "is_list" ?
197 % TODO : validation plus strict des données (pas de page négative dans les conv par exemple)
198 login = if is_list(Login) -> Login; true -> User#user.login end,
199 password = if is_list(Password) andalso Password =/= [] -> Password; true -> User#user.password end,
200 pseudo = if is_list(Pseudo) -> Pseudo; true -> User#user.pseudo end,
201 email = if is_list(Email) -> Email; true -> User#user.email end,
202 css = if is_list(Css) -> Css; true -> User#user.css end,
203 nick_format = Nick_format,
204 page_principale = if is_integer(Page_principale), Page_principale > 0 -> Page_principale; true -> User#user.page_principale end,
205 conversations = if is_list(Conversations) -> Conversations; true -> User#user.conversations end
206 },
207 mnesia:write(User_modifie),
208 ok
209 end;
210 _ -> erreur
211 end
212 end
213 ));
214 true ->
215 erreur
216 end.
217
218
219 % Met à jour la date de la dernière connexion d'un utilisateur à maintenant
220 update_date_derniere_connexion(User_id) ->
221 mnesia:transaction(
222 fun() ->
223 case mnesia:wread({user, User_id}) of
224 [User] ->
225 mnesia:write(User#user{date_derniere_connexion = now()});
226 _ ->
227 mnesia:abort("update_date_derniere_connexion: User inconnu")
228 end
229 end
230 ).
231
232
233 % Met à jour l'ip d'un user
234 update_ip(User_id, IP) ->
235 mnesia:transaction(
236 fun() ->
237 case mnesia:wread({user, User_id}) of
238 [User] ->
239 mnesia:write(User#user{last_ip = IP});
240 _ ->
241 mnesia:abort("update_ip: User inconnu")
242 end
243 end
244 ).
245
246
247 % Met à jour le pseudo du user
248 update_pseudo_user(UserId, Pseudo) ->
249 mnesia:transaction(
250 fun() ->
251 case mnesia:wread({user, UserId}) of
252 [User] when User#user.pseudo =/= Pseudo ->
253 mnesia:write(User#user{pseudo = Pseudo});
254 _ ->
255 mnesia:abort("update_pseudo_user: User inconnu ou pseudo deja à jour")
256 end
257 end
258 ).
259
260
261 % Affiche N user trié par leur date de dernière connexion.
262 % Attention : pas d'index sur ce champs (la date)
263 print_users(N) ->
264 resultat_transaction(mnesia:transaction(fun() ->
265 C = cursor(q([E || E <- qlc:keysort(9, mnesia:table(user), [{order, descending}])])),
266 Users = qlc:next_answers(C, N),
267 lists:foreach(
268 fun(U) ->
269 print_user(U)
270 end,
271 Users
272 ),
273 qlc:delete_cursor(C)
274 end)).
275
276
277 % Affiche tous les users.
278 print_users() ->
279 print_users(all_remaining).
280
281
282 print_user(User) when is_record(User, user) ->
283 #user{id = Id, pseudo = Pseudo, login = Login, ek_master = Ek_master, date_derniere_connexion = Date} = User,
284 {{Annee, Mois, Jour}, {Heure, Min, _}} = calendar:now_to_local_time(Date),
285 io:format(
286 "~4w : ~10.10..s~10.10..s ~2w.~2.2.0w.~w - ~2wh~2.2.0w~n",
287 [
288 Id,
289 if Ek_master -> "*"; true -> "" end ++ Pseudo,
290 "(" ++ Login ++ ")",
291 Jour, Mois, Annee, Heure, Min
292 ]
293 );
294 % Affichage d'un user en fonction de son login
295 print_user(Login) when is_list(Login) ->
296 case user_by_login(Login) of
297 {ok, User} ->
298 print_user(User);
299 _ ->
300 {erreur, "Login pas trouvé : " ++ Login}
301 end;
302 % Affichage d'un user en fonction de son id
303 print_user(Id) when is_integer(Id) ->
304 case user_by_id(Id) of
305 {ok, User} ->
306 print_user(User);
307 _ ->
308 {erreur, "Id pas trouvé : " ++ integer_to_list(Id)}
309 end.
310
311
312 % Est-ce qu'un utilisateur existe en fonction de son cookie ?
313 % Renvoie {ok, User} ou erreur
314 user_by_cookie(Cookie) ->
315 resultat_transaction(mnesia:transaction(
316 fun() ->
317 case e(q([E || E <- mnesia:table(user), E#user.cookie =:= Cookie])) of
318 [User] -> {ok, User};
319 _ -> erreur
320 end
321 end
322 )).
323
324
325 user_by_id(ID) ->
326 resultat_transaction(mnesia:transaction(
327 fun() ->
328 case e(q([E || E <- mnesia:table(user), E#user.id =:= ID])) of
329 [User] -> {ok, User};
330 _ -> erreur
331 end
332 end
333 )).
334
335
336 user_by_login(Login) ->
337 resultat_transaction(mnesia:transaction(
338 fun() ->
339 Users = e(q([E || E <- mnesia:table(user), E#user.login =:= Login])),
340 case Users of
341 [User] -> {ok, User};
342 _ -> erreur
343 end
344 end
345 )).
346
347
348 toggle_ek_master(User_id) ->
349 resultat_transaction(mnesia:transaction(
350 fun() ->
351 Users = e(q([E || E <- mnesia:table(user), E#user.id =:= User_id])),
352 case Users of
353 [User] ->
354 mnesia:write(User#user{ek_master = not User#user.ek_master});
355 _ -> erreur
356 end
357 end
358 )).
359
360
361 user_by_login_password(Login, Password) ->
362 resultat_transaction(mnesia:transaction(
363 fun() ->
364 case e(q([E || E <- mnesia:table(user), E#user.login =:= Login, E#user.password =:= Password])) of
365 [User | _] -> {ok, User};
366 _ -> erreur
367 end
368 end
369 )).
370
371
372 % Renvoie {ok, User} où User est un #user possédant le message donné.
373 user_by_mess(Id) ->
374 resultat_transaction(mnesia:transaction(
375 fun() ->
376 case e(q([U || U <- mnesia:table(user), M <- mnesia:table(minichat), M#minichat.id =:= Id, M#minichat.auteur_id =:= U#user.id])) of
377 [User | _] -> {ok, User};
378 _ -> erreur
379 end
380 end
381 )).
382
383
384 % Ajoute un message. Repond_A est une liste d'id auquel le message répond
385 % retourne soit l'id du message soit {erreur, <raison>}.
386 nouveau_message(Mess, Auteur_id, Repond_A) ->
387 % regarde si les id 'Repond_A' existent
388 F = fun() ->
389 Nb_id_trouve = length(e(q([E#minichat.id || E <- mnesia:table(minichat), lists:member(E#minichat.id, Repond_A)]))),
390 % est-ce que l'auteur existe ?
391 Auteur = case e(q([E || E <- mnesia:table(user), E#user.id =:= Auteur_id])) of
392 [A] -> A;
393 _ -> {erreur, "L'auteur du message est introuvable"}
394 end,
395 if Nb_id_trouve =/= length(Repond_A) ->
396 {erreur, "Un ou plusieurs messages introuvable"};
397 true ->
398 % comparaison entre la date du dernier poste et maintenant (gestion du flood)
399 Delta = delta_date_ms(Auteur#user.date_derniere_connexion, now()),
400 Nouvel_indice_flood = Auteur#user.indice_flood + if Delta =< ?DUREE_SPAM -> 2; true -> -1 end,
401 Auteur_maj = Auteur#user{
402 indice_flood = if Nouvel_indice_flood > ?INDICE_SPAM_MAX -> ?INDICE_SPAM_MAX; Nouvel_indice_flood < 0 -> 0; true -> Nouvel_indice_flood end,
403 date_derniere_connexion = now()
404 },
405 % est-ce que l'auteur à trop floodé ?
406 if Auteur#user.indice_flood =/= ?INDICE_SPAM_MAX, Auteur_maj#user.indice_flood =:= ?INDICE_SPAM_MAX, Delta =< ?DUREE_BLOCAGE_SPAM ->
407 mnesia:write(Auteur#user{indice_flood = Auteur_maj#user.indice_flood}),
408 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.");
409 Auteur#user.indice_flood =:= ?INDICE_SPAM_MAX, Delta =< ?DUREE_BLOCAGE_SPAM ->
410 {erreur, "Bloqué pour cause de flood"};
411 true ->
412 mnesia:write(Auteur_maj),
413 Id = nouvel_id(minichat),
414 inserer_reponses(Id, Repond_A),
415 mnesia:write(#minichat{id=Id, auteur_id=Auteur#user.id, date=now(), pseudo=Auteur#user.pseudo, contenu=Mess}),
416 Id
417 end
418 end
419 end,
420 resultat_transaction(mnesia:transaction(F)).
421
422 % Définit Id_repondant comme étant la réponse à Ids. Ids est une liste d'id.
423 inserer_reponses(Id_repondant, [Id_mess | Reste]) ->
424 mnesia:write(#reponse_minichat{repondant = Id_repondant, cible = Id_mess}),
425 inserer_reponses(Id_repondant, Reste);
426 inserer_reponses(_, []) ->
427 ok.
428
429
430 % Permet de créer un message système.
431 % Renvoie l'id du message système
432 nouveau_message_sys(Mess) ->
433 {ok, Root} = user_by_id(0),
434 mnesia:transaction(
435 fun() ->
436 Id = nouvel_id(minichat),
437 mnesia:write(#minichat{id=Id, auteur_id=0, date=now(), pseudo=Root#user.pseudo, contenu=Mess})
438 end
439 ).
440
441
442 % Renvoie N messages se trouvant sur la première page
443 messages(N) ->
444 messages(N, 1).
445
446
447 % Renvoie N messages se trouvant sur la page P
448 messages(N, P) ->
449 F = fun() ->
450 C = cursor(q([E || E <- qlc:keysort(2, mnesia:table(minichat), [{order, descending}])])),
451 if P > 1 -> qlc:next_answers(C, N * (P - 1));
452 true -> ok
453 end,
454 R = qlc:next_answers(C, N),
455 qlc:delete_cursor(C),
456 lists:reverse(R)
457 end,
458 resultat_transaction(mnesia:transaction(F)).
459
460
461 % Renvoie les messages manquants pour la page P en sachant qu'il y a N message
462 % par page et que le dernier message que l'on possède est Id
463 messages(Id, N, P) ->
464 lists:filter(fun (M) -> M#minichat.id > Id end, messages(N, P)).
465
466
467 % Renvoie {ok, #minichat} (voir #minichat de euphorik_bd.hrl) à partir de son id.
468 message_by_id(Id) ->
469 case resultat_transaction(mnesia:transaction(
470 fun() ->
471 e(q([E || E <- qlc:keysort(2, mnesia:table(minichat), [{order, ascending}]), Id =:= E#minichat.id]))
472 end
473 )) of
474 [M] -> {ok, M};
475 _ -> erreur
476 end.
477
478
479 % Renvoie une liste de message (voir #minichat de euphorik_bd.hrl) à partir d'une liste d'id (Ids).
480 messages_by_ids(Ids) ->
481 resultat_transaction(mnesia:transaction(
482 fun() ->
483 % TODO : optimisations ? serait-ce du O(n) ?
484 Query = q([E || E <- qlc:keysort(2, mnesia:table(minichat), [{order, ascending}]), lists:any(fun(Id) -> Id =:= E#minichat.id end, Ids)]),
485 e(Query)
486 end
487 )).
488
489
490 % Est-ce qu'un message existe ? Renvoie un boolean.
491 % TODO : ya pas plus simple ?
492 message_existe(Id) ->
493 resultat_transaction(mnesia:transaction(fun() ->
494 length(e(q([E#minichat.id || E <- mnesia:table(minichat), E#minichat.id =:= Id]))) =:= 1
495 end)).
496
497
498 % Renvoie les reponses (utilisé normalement uniquement pendant le debug).
499 reponses() ->
500 F = fun() ->
501 e(q([E || E <- mnesia:table(reponse_minichat)]))
502 end,
503 resultat_transaction(mnesia:transaction(F)).
504
505
506 % Renvoie les messages auquel M_id répond.
507 repond_a(M_id) ->
508 resultat_transaction(mnesia:transaction(
509 fun() ->
510 e(q(
511 [M || E <- mnesia:table(reponse_minichat),
512 M <- mnesia:table(minichat),
513 E#reponse_minichat.repondant =:= M_id,
514 M#minichat.id =:= E#reponse_minichat.cible]))
515 end
516 )).
517
518
519 % Est-ce que le message Id_mess est une réponse d'une message de Id_user ?
520 est_une_reponse_a_user(Id_user, Id_mess) ->
521 case mnesia:transaction(
522 fun() ->
523 e(q([
524 M#minichat.auteur_id || M <- mnesia:table(minichat), R <- mnesia:table(reponse_minichat),
525 M#minichat.auteur_id =:= Id_user, M#minichat.id =:= R#reponse_minichat.cible, R#reponse_minichat.repondant =:= Id_mess
526 ]), [{unique_all, true}])
527 end
528 ) of
529 {atomic, [_]} -> true;
530 _ -> false
531 end.
532
533
534 % Est-ce que Id_user à répondu au message Id_mess
535 a_repondu_a_message(Id_user, Id_mess) ->
536 case mnesia:transaction(
537 fun() ->
538 e(q([
539 M#minichat.auteur_id || M <- mnesia:table(minichat), R <- mnesia:table(reponse_minichat),
540 R#reponse_minichat.cible =:= Id_mess, R#reponse_minichat.repondant =:= M#minichat.id, M#minichat.auteur_id =:= Id_user
541 ]), [{unique_all, true}])
542 end
543 ) of
544 {atomic, [_]} -> true;
545 _ -> false
546 end.
547
548
549 % Est-ce que Id_user possède Id_mess ?
550 possede_message(Id_user, Id_mess) ->
551 case mnesia:transaction(
552 fun() ->
553 e(q([E#minichat.auteur_id || E <- mnesia:table(minichat), E#minichat.id =:= Id_mess]))
554 end
555 ) of
556 {atomic, [Id_user | []]} -> true;
557 _ -> false
558 end.
559
560
561 % renvoie la liste des ip bannies
562 % liste de {ip, temps_restant(en minutes), Users} ou Users est une liste de {pseudo, login}
563 % TODO : déterminer la complexité de cette fonction. (Il n'y a pas d'index sur #user.last_ip)
564 list_ban() ->
565 resultat_transaction(mnesia:transaction(
566 fun() ->
567 Now = now(),
568 e(q([
569 {
570 IP#ip_table.ip,
571 delta_date_minute(date_plus_minutes(IP#ip_table.ban, IP#ip_table.ban_duration), Now),
572 e(q([{U#user.pseudo, U#user.login} || U <- mnesia:table(user), U#user.last_ip =:= IP#ip_table.ip]))
573 } ||
574 IP <- qlc:keysort(2, mnesia:table(ip_table)),
575 if IP#ip_table.ban =:= undefined -> false; true -> date_plus_minutes(IP#ip_table.ban, IP#ip_table.ban_duration) > Now end
576 ]))
577 end
578 )).
579
580
581 % Bannie une ip pour un certain temps (en minute).
582 ban(IP, Duration) ->
583 mnesia:transaction(
584 fun() ->
585 case mnesia:wread({ip_table, IP}) of
586 [IP_tuple] ->
587 mnesia:write(IP_tuple#ip_table{ban = now(), ban_duration = Duration});
588 _ ->
589 mnesia:write(#ip_table{ip = IP, ban = now(), ban_duration = Duration})
590 end
591 end
592 ).
593
594
595 % Débanni une ip
596 deban(IP) ->
597 ban(IP, 0).
598
599
600 % Renvoie soit {true, Temps} où Temps est le temps en minutes pendant lequel le user est encore banni
601 % ou false.
602 est_banni(User_id) ->
603 resultat_transaction(mnesia:transaction(
604 fun() ->
605 case e(q([
606 {IP#ip_table.ban, IP#ip_table.ban_duration} ||
607 U <- mnesia:table(user),
608 U#user.id =:= User_id,
609 IP <- mnesia:table(ip_table),
610 IP#ip_table.ip =:= U#user.last_ip
611 ])) of
612 [{Ban, Ban_duration}] ->
613 Echeance = date_plus_minutes(Ban, Ban_duration),
614 Now = now(),
615 if Echeance < Now -> % l'échéance est passée
616 false;
617 true ->
618 {true, delta_date_minute(Echeance, Now)}
619 end;
620 _ ->
621 false
622 end
623 end
624 )).
625
626
627 % Ban est une date tel que retourner par now().
628 % Ban_duration est un temps en minutes.
629 % retourne une date.
630 date_plus_minutes(Ban, Ban_duration) ->
631 Duration_sec = Ban_duration * 60,
632 {MegaSec, Sec, MicroSec} = Ban,
633 {MegaSec + if Sec + Duration_sec >= 1000000 -> 1; true -> 0 end,(Sec + Duration_sec) rem 1000000, MicroSec}.
634
635
636 % Si deux enregistrements consequtifs de la même IP sont fait en moins d'une seconde alors
637 % ip_table.nb_try_register est incrémenté de 1 sinon il est décrémenté de 1 (jusqu'a 0).
638 % Si ip_table.nb_try_register vaut 5 alors l'ip ne peux plus s'enregistrer pour une heure.
639 can_register(IP) ->
640 resultat_transaction(mnesia:transaction(
641 fun() ->
642 case e(q([I || I <- mnesia:table(ip_table), I#ip_table.ip =:= IP])) of
643 [] ->
644 mnesia:write(#ip_table{ip = IP, date_last_try_register = now()}),
645 true;
646 [T] ->
647 Delta = delta_date_ms(T#ip_table.date_last_try_register, now()),
648 if T#ip_table.nb_try_register =:= ?NB_MAX_FLOOD_REGISTER, Delta < ?TEMPS_BAN_FLOOD_REGISTER ->
649 false;
650 true ->
651 mnesia:write(#ip_table{
652 ip = IP,
653 date_last_try_register = now(),
654 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
655 }),
656 true
657 end
658 end
659 end
660 )).
661
662
663 % Renvoie tous les trolls
664 trolls() ->
665 resultat_transaction(mnesia:transaction(
666 fun() ->
667 e(q([T || T <- qlc:keysort(2, mnesia:table(troll))]))
668 end
669 )).
670
671
672 % Renvoie les trolls manquants posté après Last_id.
673 trolls(Last_id) ->
674 resultat_transaction(mnesia:transaction(
675 fun() ->
676 e(q([T || T <- qlc:keysort(2, mnesia:table(troll)), T#troll.id > Last_id, T#troll.date_post =:= undefined]))
677 end
678 )).
679
680
681 % Renvoie l'id du nouveau troll
682 % ou max_troll_reached_per_user si le nombre de troll posté par l'utilisateur max a été atteind
683 % ou max_troll_reached si le nombre de troll posté max a été atteind
684 % ou user_unknown
685 put_troll(User_id, Content) ->
686 resultat_transaction(mnesia:transaction(
687 fun() ->
688 % control le nombre de troll déjà posté
689 Nb_troll_poste_par_user = length(e(q(
690 [
691 E#troll.id || E <- mnesia:table(troll),
692 E#troll.id_user =:= User_id,
693 E#troll.date_post =:= undefined
694 ]
695 ))),
696 Nb_troll_poste_total = length(e(q(
697 [
698 E#troll.id || E <- mnesia:table(troll),
699 E#troll.date_post =:= undefined
700 ]
701 ))),
702 User = user_by_id(User_id),
703 case User of
704 {ok, _} ->
705 if Nb_troll_poste_par_user >= ?NB_MAX_TROLL_WAITING_BY_USER ->
706 max_troll_reached_per_user;
707 Nb_troll_poste_total >= ?NB_MAX_TROLL_WAITING ->
708 max_troll_reached;
709 true ->
710 Id = nouvel_id(minichat),
711 mnesia:write(#troll{id = Id, id_user = User_id, date_create = now(), content = Content}),
712 Id
713 end;
714 _ ->
715 user_unknown
716 end
717 end
718 )).
719
720
721 % renvoie ok | erreur
722 mod_troll(Troll_id, Content) ->
723 mnesia:transaction(
724 fun() ->
725 case mnesia:wread({troll, Troll_id}) of
726 [Troll = #troll{date_post = undefined}] ->
727 mnesia:write(Troll#troll{content = Content});
728 _ ->
729 mnesia:abort("mod_troll: Troll inconnu ou déjà posté")
730 end
731 end
732 ).
733
734
735 del_troll(Troll_id) ->
736 mnesia:transaction(
737 fun() ->
738 case mnesia:wread({troll, Troll_id}) of
739 [#troll{date_post = undefined}] ->
740 mnesia:delete({troll, Troll_id});
741 _ ->
742 mnesia:abort("mod_troll: Troll inconnu ou déjà posté")
743 end
744 end
745 ).
746
747
748 troll_by_id(Troll_id) ->
749 resultat_transaction(mnesia:transaction(
750 fun() ->
751 case e(q([T || T <- mnesia:table(troll), T#troll.id =:= Troll_id])) of
752 [T] -> {ok, T};
753 _ ->
754 erreur
755 end
756 end
757 )).
758
759
760 % Renvoie le troll actuel qui se trouve sur la page principale.
761 % Renvois aucun si pas de troll courant.
762 current_troll() ->
763 resultat_transaction(mnesia:transaction(
764 fun() ->
765 % TODO : ya pas moyen de désigner le champs plutot qu'avec un nombre pour le tri ?
766 C = cursor(q([T || T <- qlc:keysort(5, mnesia:table(troll), [{order, descending}]), T#troll.date_post =/= undefined])),
767 R = case qlc:next_answers(C, 1) of
768 [T] -> T;
769 _ -> aucun
770 end,
771 qlc:delete_cursor(C),
772 R
773 end
774 )).
775
776
777 % Elit un troll au hasard parmis les trolls en attente (leur date_post =:= undefined)
778 % met à jour sa date de post.
779 % renvoie plus_de_trolls si il n'y a aucun troll en attente.
780 elire_troll() ->
781 {A1,A2,A3} = now(),
782 random:seed(A1, A2, A3),
783 mnesia:transaction(
784 fun() ->
785 case e(q([T || T <- mnesia:table(troll), T#troll.date_post =:= undefined])) of
786 [] ->
787 plus_de_trolls;
788 Trolls ->
789 Troll = lists:nth(random:uniform(length(Trolls)), Trolls),
790 Troll2 = Troll#troll{date_post = now()},
791 mnesia:write(Troll2)
792 end
793 end
794 ).
795
796
797
798 update_version(1) ->
799 mnesia:transform_table(
800 ip_table,
801 fun() -> null end,
802 record_info(fields, ip_table),
803 ip_table
804 ).
805
806
807 % Renvoie le résultat d'une transaction (en décomposant le tuple fournit)
808 resultat_transaction({_, T}) ->
809 T.
810
811
812 % Retourne la difference entre deux timestamp (erlang:now()) en miliseconde
813 delta_date_ms(D1, D2) ->
814 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).
815
816 % Retourne la différence entre deux timestamp (erlang:now) en minutes
817 delta_date_minute(D1, D2) ->
818 trunc(delta_date_ms(D1, D2) / 1000 / 60).
819
820
821 % Bizarre, cette fonction n'existe pas dans la stdlib.
822 % Pas utilisé mais bon ca me fait de la peine de l'enlever.
823 ceiling(X) ->
824 T = trunc(X),
825 case (X - T) of
826 Neg when Neg < 0 -> T;
827 Pos when Pos > 0 -> T + 1;
828 _ -> T
829 end.
830
831
832 % Renvoie un nouvel id pour une table donnée
833 nouvel_id(Table) ->
834 mnesia:dirty_update_counter(counter, Table, 1).
835