+ Conv\r
+ )\r
+ }}\r
+ ]}\r
+ end,\r
+ % on ajoute un 'undefined' correspondant à la premier conversation qui ne possède pas de racine\r
+ % TODO : peut être à revoir car un peu lourd est compliqué\r
+ aggregation_racines_conversations([undefined | Racines_conversations], Conversations)\r
+ )\r
+ }}\r
+ ]}\r
+ end\r
+ end.\r
+ \r
+\r
+aggregation_racines_conversations(L1, L2) -> \r
+ aggregation_racines_conversations(L1, L2, []).\r
+aggregation_racines_conversations([], [], L) -> lists:reverse(L);\r
+aggregation_racines_conversations([E1|R1], [E2|R2], L) ->\r
+ aggregation_racines_conversations(R1, R2, [{E1, E2} | L]).\r
+ \r
+\r
+\r
+% Attend un événement lié à la page 'chat'.\r
+wait_event_bd_page_chat() ->\r
+ receive % attente d'un post\r
+ {mnesia_table_event, {write, minichat, _Message, [], _}} ->\r
+ ok;\r
+ {mnesia_table_event, {write, troll, Troll, [Old_troll | _], _}} when Troll#troll.date_post =/= undefined, Old_troll#troll.date_post == undefined ->\r
+ ok;\r
+ {tcp_closed, _} ->\r
+ exit(normal);\r
+ _ ->\r
+ wait_event_bd_page_chat()\r
+ % 60 minutes de timeout (on ne sais jamais)\r
+ % Après 60 minutes de connexion, le client doit donc reétablir une connexion\r
+ after 1000 * 60 * 60 -> \r
+ timeout\r
+ end.\r
+\r
+\r
+% Attent un événement concernant la page admin\r
+% Renvoie les trolls manquants posté après Last_id ou banned_ips_refresh.\r
+% Si pas de trolls alors attend un événement tel qu'un ajout, une modification ou une suppression.\r
+% renvoie :\r
+% {mod, Troll}\r
+% ou {add, [Trolls]}\r
+% ou {del, Troll_id}\r
+% ou banned_ips_refresh\r
+% ou timeout\r
+wait_event_page_admin(Last_id) ->\r
+ case {mnesia:subscribe({table, troll, detailed}), mnesia:subscribe({table, ip_table, detailed})} of\r
+ {{error, E}, _ } -> E;\r
+ {_, {error, E}} -> E;\r
+ _ ->\r
+ R = case euphorik_bd:trolls(Last_id) of\r
+ [] -> % pas de trolls\r
+ wait_event_page_admin();\r
+ Trolls ->\r
+ {add, Trolls}\r
+ end,\r
+ mnesia:unsubscribe({table, troll, detailed}),\r
+ mnesia:unsubscribe({table, ip_table, detailed}),\r
+ R\r
+ end.\r
+ \r
+wait_event_page_admin() ->\r
+ % s'il n'y a pas de trolls que l'utilisateur n'a pas connaissance alors on attend un événement\r
+ receive\r
+ % cas où un troll est choisit comme courant\r
+ {mnesia_table_event, {write, troll, Troll, [Old_troll | _], _}}\r
+ when Old_troll#troll.date_post =:= undefined, Troll#troll.date_post =/= undefined ->\r
+ {del, Troll#troll.id};\r
+ {mnesia_table_event, {write, troll, Troll, [_Old_troll | _], _}} ->\r
+ {mod, Troll};\r
+ {mnesia_table_event, {write, troll, Troll, [], _}} ->\r
+ {add, [Troll]};\r
+ {mnesia_table_event, {delete, troll, {troll, Id}, _, _}} ->\r
+ {del, Id};\r
+ {mnesia_table_event, {write, ip_table, IP, [Old_IP | _], _}}\r
+ when Old_IP#ip_table.ban =/= IP#ip_table.ban; Old_IP#ip_table.ban_duration =/= IP#ip_table.ban_duration ->\r
+ banned_ips_refresh;\r
+ {tcp_closed, _} ->\r
+ exit(normal);\r
+ _ ->\r
+ wait_event_page_admin()\r
+ % 60 minutes de timeout (on ne sais jamais)\r
+ % Après 60 minutes de connexion, le client doit donc reétablir une connexion\r
+ after 1000 * 60 * 60 -> \r
+ timeout\r
+ end.\r
+ \r
+ \r
+% Un utilisateur envoie un message\r
+% Answer_to est une liste d'id (int)\r
+put_message(\r
+ [\r
+ {cookie, Cookie},\r
+ {nick, Nick},\r
+ {content, Content},\r
+ {answer_to, {array, Answer_to}}\r
+ ]\r
+) ->\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, User} ->\r
+ case euphorik_bd:est_banni(User#user.id) of\r
+ {true, Temps_restant} ->\r
+ erreur(80, format_minutes(Temps_restant));\r
+ _ ->\r
+ Strip_content = string:strip(Content),\r
+ if Strip_content =:= [] ->\r
+ erreur(90);\r
+ true ->\r
+ % attention : non-atomique (update_pseudo+nouveau_message)\r
+ euphorik_bd:update_pseudo_user(User#user.id, Nick),\r
+ case euphorik_bd:nouveau_message(Strip_content, User#user.id, Answer_to) of\r
+ {erreur, R} -> erreur(100, R);\r
+ _ ->\r
+ json_reponse_ok()\r
+ end\r
+ end\r
+ end;\r
+ _ ->\r
+ erreur(110)\r
+ end.\r
+\r
+\r
+% bannissement d'un utilisateur (son ip est bannie)\r
+ban(\r
+ [\r
+ {cookie, Cookie},\r
+ {duration, Duration},\r
+ {user_id, User_id},\r
+ {reason, Reason}\r
+ ]) ->\r
+ % controle que l'utilisateur est un admin\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, User1 = #user{ek_master = true}} ->\r
+ case euphorik_bd:user_by_id(User_id) of\r
+ {ok, User1} ->\r
+ erreur(120);\r
+ {ok, #user{ek_master = false, profile = Profile2} = User2} ->\r
+ euphorik_bd:ban(User2#user.last_ip, Duration),\r
+ euphorik_bd:nouveau_message_sys(lists:flatten(io_lib:format("''~s~s'' est ~s pour ~s.~s",\r
+ [\r
+ Profile2#profile.pseudo,\r
+ if User2#user.login =:= [] -> ""; true -> " (" ++ User2#user.login ++ ")" end,\r
+ if Duration =< 15 -> "kické"; true -> "banni" end,\r
+ format_minutes(Duration),\r
+ if Reason =/= [] -> " - Raison: " ++ Reason; true -> "" end ++ "."\r
+ ]\r
+ ))),\r
+ json_reponse_ok();\r
+ {ok, _} ->\r
+ erreur(130);\r
+ _ ->\r
+ erreur(140)\r
+ end;\r
+ _ ->\r
+ erreur(150)\r
+ end.\r
+ \r
+\r
+% slapage d'un user (avertissement)\r
+slap(\r
+ [\r
+ {cookie, Cookie},\r
+ {user_id, User_id},\r
+ {reason, Reason}\r
+ ]) ->\r
+ % controle que l'utilisateur est un admin\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, User1 = #user{ek_master = true, profile = Profile1}} ->\r
+ case euphorik_bd:user_by_id(User_id) of\r
+ {ok, User1} ->\r
+ euphorik_bd:nouveau_message_sys(lists:flatten(io_lib:format("~s s'auto slap~s.", \r
+ [\r
+ Profile1#profile.pseudo,\r
+ if Reason =/= [] -> " - Raison: " ++ Reason; true -> "" end\r
+ ]\r
+ ))),\r
+ json_reponse_ok();\r
+ {ok, #user{ek_master = false, profile = Profile2}} ->\r
+ euphorik_bd:nouveau_message_sys(lists:flatten(io_lib:format("~s se fait slaper par ~s.~s",\r
+ [\r
+ Profile2#profile.pseudo,\r
+ Profile1#profile.pseudo,\r
+ if Reason =/= [] -> " - Raison: " ++ Reason; true -> "" end ++ "."\r
+ ]\r
+ ))),\r
+ json_reponse_ok();\r
+ {ok, _} ->\r
+ erreur(130);\r
+ _ ->\r
+ erreur(160)\r
+ end;\r
+ _ ->\r
+ erreur(170)\r
+ end.\r
+ \r
+ \r
+put_troll(\r
+ [\r
+ {cookie, Cookie},\r
+ {content, Content}\r
+ ]\r
+) ->\r
+ % controle que l'utilisateur est un admin\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, User = #user{ek_master = true}} ->\r
+ case euphorik_bd:put_troll(User#user.id, Content) of\r
+ max_troll_reached_per_user ->\r
+ erreur(180, ?NB_MAX_TROLL_WAITING_BY_USER);\r
+ max_troll_reached ->\r
+ erreur(190, ?NB_MAX_TROLL_WAITING);\r
+ _Id ->\r
+ json_reponse_ok()\r
+ end;\r
+ _ ->\r
+ erreur(200)\r
+ end.\r
+ \r
+ \r
+mod_troll(\r
+ [\r
+ {cookie, Cookie},\r
+ {troll_id, Troll_id},\r
+ {content, Content}\r
+ ]\r
+) ->\r
+ % controle que l'utilisateur est un admin\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, User = #user{ek_master = true}} ->\r
+ User_id = User#user.id,\r
+ case euphorik_bd:troll_by_id(Troll_id) of\r
+ {ok, #troll{id_user = User_id}} ->\r
+ euphorik_bd:mod_troll(Troll_id, Content),\r
+ json_reponse_ok();\r
+ _ ->\r
+ erreur(210)\r
+ end;\r
+ _ ->\r
+ erreur(220)\r
+ end.\r
+\r
+ \r
+del_troll(\r
+ [\r
+ {cookie, Cookie},\r
+ {troll_id, Troll_id}\r
+ ]\r
+) -> \r
+ % controle que l'utilisateur est un admin\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, User = #user{ek_master = true}} ->\r
+ User_id = User#user.id,\r
+ case euphorik_bd:troll_by_id(Troll_id) of\r
+ {ok, #troll{id_user = User_id}} ->\r
+ euphorik_bd:del_troll(Troll_id),\r
+ json_reponse_ok();\r
+ _ ->\r
+ erreur(210)\r
+ end;\r
+ _ ->\r
+ erreur(220)\r
+ end.\r
+ \r
+ \r
+unban_ip(\r
+ [\r
+ {cookie, Cookie},\r
+ {ip, IP}\r
+ ]\r
+) ->\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, #user{ek_master = true}} ->\r
+ euphorik_bd:deban(euphorik_common:unserialize_ip(IP)),\r
+ json_reponse_ok();\r
+ _ ->\r
+ erreur(230)\r
+ end.\r
+ \r
+ \r
+list_banned_ips(\r
+ [\r
+ {cookie, Cookie}\r
+ ]\r
+) ->\r
+ case euphorik_bd:user_by_cookie(Cookie) of\r
+ {ok, #user{ek_master = true}} ->\r
+ {\r
+ struct,\r
+ [\r
+ {reply, "list_banned_ips"},\r
+ {list, {array, lists:map(\r
+ fun({IP, T, Users}) ->\r
+ {struct,\r
+ [\r
+ {ip, euphorik_common:serialize_ip(IP)},\r
+ {remaining_time, format_minutes(T)},\r
+ {users, {array, lists:map(\r
+ fun({Pseudo, Login}) ->\r
+ {struct,\r