ADD minicommentaire
[euphorik.git] / modules / erl / euphorik_protocole.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 gére les différents messages envoyés par le client (javascript) via AJAX.
20 % Les messages donnés ainsi que les réponses sont au format JSON.
21 % @author G.Burri
22
23
24 -module(euphorik_protocole).
25 -export([
26 register/2,
27 login/2,
28 logout/1,
29 profile/1,
30 wait_event/1,
31 put_message/1,
32 ban/1,
33 slap/1,
34 put_troll/1,
35 mod_troll/1,
36 del_troll/1,
37 unban_ip/1,
38 list_banned_ips/1,
39 erreur/1
40 ]).
41 -include("../include/euphorik_bd.hrl").
42 -include("../include/euphorik_defines.hrl").
43
44
45 % Une utilisateur s'enregistre avec un tuple {Login, Password}.
46 register([{login, Login}, {password, Password}], IP) ->
47 Can_register = euphorik_bd:can_register(IP),
48 if Can_register ->
49 case euphorik_bd:user_by_login(Login) of
50 {ok, _} ->
51 erreur("Login déjà existant");
52 _ ->
53 User = euphorik_bd:nouveau_user(Login, Password, generer_cookie()),
54 euphorik_bd:update_ip(User#user.id, IP),
55 json_reponse_login_ok(User)
56 end;
57 true ->
58 erreur_register_flood()
59 end;
60 % Enregistrement sans {Login, Password}
61 register([], IP) ->
62 Can_register = euphorik_bd:can_register(IP),
63 if Can_register ->
64 User = euphorik_bd:nouveau_user("<nick>", generer_cookie()),
65 euphorik_bd:update_ip(User#user.id, IP),
66 json_reponse_login_ok(User);
67 true ->
68 erreur_register_flood()
69 end.
70
71 erreur_register_flood() ->
72 erreur("Trop de register (flood)").
73
74
75 % Un utilisateur se logge (avec un couple {login, mot de passe})
76 login([{login, Login}, {password, Password}], IP) ->
77 case euphorik_bd:user_by_login_password(Login, Password) of
78 {ok, User} ->
79 loginUser(User, IP);
80 _ ->
81 timer:sleep(?TEMPS_ATTENTE_ERREUR_LOGIN),
82 erreur("Couple login/pass introuvable")
83 end;
84 % Un utilisateur se logge (avec un cookie)
85 login([{cookie, Cookie}], IP) ->
86 case euphorik_bd:user_by_cookie(Cookie) of
87 {ok, User} ->
88 loginUser(User, IP);
89 _ ->
90 timer:sleep(?TEMPS_ATTENTE_ERREUR_LOGIN),
91 erreur("Authentification impossible par cookie")
92 end.
93
94 loginUser(User, IP) ->
95 euphorik_bd:update_ip(User#user.id, IP),
96 euphorik_bd:update_date_derniere_connexion(User#user.id),
97 json_reponse_login_ok(User).
98
99
100 % Renvoie un string() représentant un cookie en base 36. Il y a 10^32 possibillités.
101 generer_cookie() ->
102 {A1, A2, A3} = now(),
103 random:seed(A1, A2, A3),
104 erlang:integer_to_list(random:uniform(math:pow(10, 32)), 36).
105
106
107 % Un utilisateur se délogge.
108 logout(_) ->
109 do_nothing.
110
111
112 % Modification du profile.
113 profile(
114 [
115 {cookie, Cookie},
116 {login, Login},
117 {password, Password},
118 {nick, Pseudo},
119 {email, Email},
120 {css, Css},
121 {chat_order, Chat_order_str},
122 {nick_format, Nick_format_str},
123 {view_times, View_times},
124 {view_tooltips, View_tooltips},
125 {conversations, {array, Conversations_json}},
126 {ostentatious_master, Ostentatious_master}
127 ]
128 ) ->
129 % décomposition de la strucure JSON
130 Conversations = lists:foldr(
131 fun({struct, [{root, Racine}, {minimized, Reduit}]}, A) ->
132 % virage des messages qui n'existent pas
133 Message_exite = euphorik_bd:message_existe(Racine),
134 if Message_exite ->
135 [ {Racine, Reduit} | A];
136 true ->
137 A
138 end
139 end,
140 [],
141 Conversations_json
142 ),
143 % TODO : pas très beau, mieux vaut construire un #user
144 case euphorik_bd:set_profile(
145 Cookie,
146 Login,
147 Password,
148 Pseudo,
149 Email,
150 Css,
151 list_to_atom(Chat_order_str),
152 list_to_atom(Nick_format_str),
153 View_times,
154 View_tooltips,
155 Conversations,
156 list_to_atom(Ostentatious_master)) of
157 ok ->
158 json_reponse_ok();
159 login_deja_pris ->
160 erreur("Login déjà pris");
161 _ ->
162 erreur("Impossible de mettre à jour le profile")
163 end.
164
165
166 % Renvoie les messages appropriés.
167 % last_message id et cookie sont facultatifs
168 wait_event([{page, "chat"} | Data]) ->
169 % traitement des inputs
170 Cookie = case lists:keysearch(cookie, 1, Data) of {value, {_, C}} -> C; _ -> inconnu end,
171 Last_message_id = case lists:keysearch(last_message_id, 1, Data) of {value, {_, Id}} -> Id; _ -> 0 end,
172 {value, {_, Message_count}} = lists:keysearch(message_count, 1, Data),
173 Main_page = case lists:keysearch(main_page, 1, Data) of {value, {_, P}} -> P; _ -> 1 end,
174 Troll_id = case lists:keysearch(troll_id, 1, Data) of {value, {_, T}} -> T; _ -> 0 end,
175 {value, {_, {array, Conversations_json}}} = lists:keysearch(conversations, 1, Data),
176 Racines_conversations = lists:map(
177 fun({struct, [{root, Racine}, {page, Page} | Reste]}) ->
178 Last_mess_conv = case Reste of [{last_message_id, L}] -> L; _ -> 0 end,
179 {Racine, Page, Last_mess_conv}
180 end,
181 Conversations_json
182 ),
183 User = case euphorik_bd:user_by_cookie(Cookie) of
184 {ok, U} -> U;
185 _ -> inconnu
186 end,
187 case {mnesia:subscribe({table, minichat, detailed}), mnesia:subscribe({table, troll, detailed})} of
188 {{error, E}, _} -> E;
189 {_, {error, E}} -> E;
190 _ ->
191 % attente d'événements
192 R = wait_event_page_chat(User, Racines_conversations, Message_count, Last_message_id, Main_page, Troll_id),
193 mnesia:unsubscribe({table, minichat, detailed}),
194 mnesia:unsubscribe({table, troll, detailed}),
195 R
196 end;
197 wait_event([{page, "admin"}, {last_troll, Last_troll}]) ->
198 case wait_event_page_admin(Last_troll) of
199 banned_ips_refresh ->
200 {struct,
201 [
202 {reply, "banned_ips_refresh"}
203 ]
204 };
205 {mod, Troll} ->
206 {struct,
207 [
208 {reply, "troll_modified"},
209 {troll_id, Troll#troll.id},
210 {content, Troll#troll.content}
211 ]
212 };
213 {add, Trolls} ->
214 {struct,
215 [
216 {reply, "troll_added"},
217 {trolls, {array,
218 lists:map(
219 fun(T) ->
220 {ok, User} = euphorik_bd:user_by_id(T#troll.id_user),
221 {struct,
222 [
223 {troll_id, T#troll.id},
224 {content, T#troll.content},
225 {author, User#user.pseudo},
226 {author_id, User#user.id}
227 ]
228 }
229 end,
230 Trolls
231 )
232 }}
233 ]
234 };
235 {del, Troll_id} ->
236 {struct,
237 [
238 {reply, "troll_deleted"},
239 {troll_id, Troll_id}
240 ]
241 };
242 _ ->
243 erreur("timeout")
244 end;
245 wait_event(_) ->
246 erreur("Page inconnue").
247
248
249 wait_event_page_chat(User, Racines_conversations, Message_count, Last_message_id, Main_page, Troll_id) ->
250 % est-ce que le troll est à jour ?
251 case euphorik_bd:current_troll() of
252 Current when is_record(Current, troll), Current#troll.id =/= Troll_id ->
253 {struct, [
254 {reply, "new_troll"},
255 {troll_id, Current#troll.id},
256 {message_id, euphorik_bd:message_id_associe(Current#troll.id)},
257 {content, Current#troll.content}
258 ]};
259 _ ->
260 % est-ce qu'il y a des nouveaux messages ?
261 case euphorik_minichat_conversation:conversations(Racines_conversations, Message_count, Last_message_id, Main_page) of
262 vide ->
263 wait_event_bd_page_chat(),
264 wait_event_page_chat(User, Racines_conversations, Message_count, Last_message_id, Main_page, Troll_id);
265 Conversations ->
266 % accrochez-vous ca va siouxer ;)
267 {struct, [
268 {reply, "new_messages"},
269 {conversations, {array,
270 lists:map(
271 fun({Racine, {Conv, Plus}}) ->
272 {struct, [
273 {last_page, not Plus},
274 {first, % le premier message de la conversation
275 if Racine =:= undefined orelse Conv =:= [] ->
276 null;
277 true ->
278 {Racine_id, _, _} = Racine,
279 case euphorik_bd:message_by_id(Racine_id) of
280 {ok, Mess} ->
281 json_message(Mess, euphorik_bd:parents(Racine), User);
282 _ ->
283 null
284 end
285 end
286 },
287 {messages, {array,
288 lists:map(
289 fun({Mess, Repond_a}) ->
290 json_message(Mess, Repond_a, User)
291 end,
292 Conv
293 )
294 }}
295 ]}
296 end,
297 % on ajoute un 'undefined' correspondant à la premier conversation qui ne possède pas de racine
298 % TODO : peut être à revoir car un peu lourd est compliqué
299 aggregation_racines_conversations([undefined | Racines_conversations], Conversations)
300 )
301 }}
302 ]}
303 end
304 end.
305
306
307 aggregation_racines_conversations(L1, L2) ->
308 aggregation_racines_conversations(L1, L2, []).
309 aggregation_racines_conversations([], [], L) -> lists:reverse(L);
310 aggregation_racines_conversations([E1|R1], [E2|R2], L) ->
311 aggregation_racines_conversations(R1, R2, [{E1, E2} | L]).
312
313
314
315 % Attend un événement lié à la page 'chat'.
316 wait_event_bd_page_chat() ->
317 receive % attente d'un post
318 {mnesia_table_event, {write, minichat, _Message, [], _}} ->
319 ok;
320 {mnesia_table_event, {write, troll, Troll, [Old_troll | _], _}} when Troll#troll.date_post =/= undefined, Old_troll#troll.date_post == undefined ->
321 ok;
322 {tcp_closed, _} ->
323 exit(normal);
324 _ ->
325 wait_event_bd_page_chat()
326 % 60 minutes de timeout (on ne sais jamais)
327 % Après 60 minutes de connexion, le client doit donc reétablir une connexion
328 after 1000 * 60 * 60 ->
329 timeout
330 end.
331
332
333 % Attent un événement concernant la page admin
334 % Renvoie les trolls manquants posté après Last_id ou banned_ips_refresh.
335 % Si pas de trolls alors attend un événement tel qu'un ajout, une modification ou une suppression.
336 % renvoie :
337 % {mod, Troll}
338 % ou {add, [Trolls]}
339 % ou {del, Troll_id}
340 % ou banned_ips_refresh
341 % ou timeout
342 wait_event_page_admin(Last_id) ->
343 case {mnesia:subscribe({table, troll, detailed}), mnesia:subscribe({table, ip_table, detailed})} of
344 {{error, E}, _ } -> E;
345 {_, {error, E}} -> E;
346 _ ->
347 R = case euphorik_bd:trolls(Last_id) of
348 [] -> % pas de trolls
349 wait_event_page_admin();
350 Trolls ->
351 {add, Trolls}
352 end,
353 mnesia:unsubscribe({table, troll, detailed}),
354 mnesia:unsubscribe({table, ip_table, detailed}),
355 R
356 end.
357
358 wait_event_page_admin() ->
359 % s'il n'y a pas de trolls que l'utilisateur n'a pas connaissance alors on attend un événement
360 receive
361 % cas où un troll est choisit comme courant
362 {mnesia_table_event, {write, troll, Troll, [Old_troll | _], _}}
363 when Old_troll#troll.date_post =:= undefined, Troll#troll.date_post =/= undefined ->
364 {del, Troll#troll.id};
365 {mnesia_table_event, {write, troll, Troll, [_Old_troll | _], _}} ->
366 {mod, Troll};
367 {mnesia_table_event, {write, troll, Troll, [], _}} ->
368 {add, [Troll]};
369 {mnesia_table_event, {delete, troll, {troll, Id}, _, _}} ->
370 {del, Id};
371 {mnesia_table_event, {write, ip_table, IP, [Old_IP | _], _}}
372 when Old_IP#ip_table.ban =/= IP#ip_table.ban; Old_IP#ip_table.ban_duration =/= IP#ip_table.ban_duration ->
373 banned_ips_refresh;
374 {tcp_closed, _} ->
375 exit(normal);
376 _ ->
377 wait_event_page_admin()
378 % 60 minutes de timeout (on ne sais jamais)
379 % Après 60 minutes de connexion, le client doit donc reétablir une connexion
380 after 1000 * 60 * 60 ->
381 timeout
382 end.
383
384
385 % Un utilisateur envoie un message
386 % Answer_to est une liste d'id (int)
387 put_message(
388 [
389 {cookie, Cookie},
390 {nick, Nick},
391 {content, Content},
392 {answer_to, {array, Answer_to}}
393 ]
394 ) ->
395 case euphorik_bd:user_by_cookie(Cookie) of
396 {ok, User} ->
397 case euphorik_bd:est_banni(User#user.id) of
398 {true, Temps_restant} ->
399 erreur("Vous êtes banni pour encore " ++ format_minutes(Temps_restant));
400 _ ->
401 Strip_content = string:strip(Content),
402 if Strip_content =:= [] ->
403 erreur("Message vide");
404 true ->
405 % attention : non-atomique (update_pseudo+nouveau_message)
406 euphorik_bd:update_pseudo_user(User#user.id, Nick),
407 case euphorik_bd:nouveau_message(Strip_content, User#user.id, Answer_to) of
408 {erreur, R} -> erreur("Impossible d'ajouter un nouveau message. Raison : " ++ R);
409 _ ->
410 json_reponse_ok()
411 end
412 end
413 end;
414 _ ->
415 erreur("Utilisateur inconnu")
416 end.
417
418
419 % bannissement d'un utilisateur (son ip est bannie)
420 ban(
421 [
422 {cookie, Cookie},
423 {duration, Duration},
424 {user_id, User_id},
425 {reason, Reason}
426 ]) ->
427 % controle que l'utilisateur est un admin
428 case euphorik_bd:user_by_cookie(Cookie) of
429 {ok, User1 = #user{ek_master = true}} ->
430 case euphorik_bd:user_by_id(User_id) of
431 {ok, User1} ->
432 erreur("Il n'est pas possible de s'auto bannir");
433 {ok, User2 = #user{ek_master = false}} ->
434 euphorik_bd:ban(User2#user.last_ip, Duration),
435 euphorik_bd:nouveau_message_sys(lists:flatten(io_lib:format("''~s~s'' est ~s pour ~s.~s",
436 [
437 User2#user.pseudo,
438 if User2#user.login =:= [] -> ""; true -> " (" ++ User2#user.login ++ ")" end,
439 if Duration =< 15 -> "kické"; true -> "banni" end,
440 format_minutes(Duration),
441 if Reason =/= [] -> " - Raison: " ++ Reason; true -> "" end ++ "."
442 ]
443 ))),
444 json_reponse_ok();
445 {ok, _} ->
446 erreur("L'utilisateur est lui même un ekMaster");
447 _ ->
448 erreur("Utilisateur à bannir inconnu")
449 end;
450 _ ->
451 erreur("Utilisateur inconnu ou non ek master")
452 end.
453
454
455 % slapage d'un user (avertissement)
456 slap(
457 [
458 {cookie, Cookie},
459 {user_id, User_id},
460 {reason, Reason}
461 ]) ->
462 % controle que l'utilisateur est un admin
463 case euphorik_bd:user_by_cookie(Cookie) of
464 {ok, User1 = #user{ek_master = true}} ->
465 case euphorik_bd:user_by_id(User_id) of
466 {ok, User1} ->
467 euphorik_bd:nouveau_message_sys(lists:flatten(io_lib:format("~s s'auto slap~s.",
468 [
469 User1#user.pseudo,
470 if Reason =/= [] -> " - Raison: " ++ Reason; true -> "" end
471 ]
472 ))),
473 json_reponse_ok();
474 {ok, User2 = #user{ek_master = false}} ->
475 euphorik_bd:nouveau_message_sys(lists:flatten(io_lib:format("~s se fait slaper par ~s.~s",
476 [
477 User2#user.pseudo,
478 User1#user.pseudo,
479 if Reason =/= [] -> " - Raison: " ++ Reason; true -> "" end ++ "."
480 ]
481 ))),
482 json_reponse_ok();
483 {ok, _} ->
484 erreur("L'utilisateur est lui même un ekMaster");
485 _ ->
486 erreur("Utilisateur à slaper inconnu")
487 end;
488 _ ->
489 erreur("Utilisateur inconnu ou non ek master")
490 end.
491
492
493 put_troll(
494 [
495 {cookie, Cookie},
496 {content, Content}
497 ]
498 ) ->
499 % controle que l'utilisateur est un admin
500 case euphorik_bd:user_by_cookie(Cookie) of
501 {ok, User = #user{ek_master = true}} ->
502 case euphorik_bd:put_troll(User#user.id, Content) of
503 max_troll_reached_per_user ->
504 erreur(lists:flatten(io_lib:format("Le nombre de troll maximum par utilisateur est atteint : ~w ", [?NB_MAX_TROLL_WAITING_BY_USER])));
505 max_troll_reached ->
506 erreur(lists:flatten(io_lib:format("Le nombre de troll maximum en attente est atteint : ~w ", [?NB_MAX_TROLL_WAITING])));
507 _Id ->
508 json_reponse_ok()
509 end;
510 _ ->
511 erreur("Seul les ekMaster peuvent proposer des trolls")
512 end.
513
514
515 mod_troll(
516 [
517 {cookie, Cookie},
518 {troll_id, Troll_id},
519 {content, Content}
520 ]
521 ) ->
522 % controle que l'utilisateur est un admin
523 case euphorik_bd:user_by_cookie(Cookie) of
524 {ok, User = #user{ek_master = true}} ->
525 User_id = User#user.id,
526 case euphorik_bd:troll_by_id(Troll_id) of
527 {ok, #troll{id_user = User_id}} ->
528 euphorik_bd:mod_troll(Troll_id, Content),
529 json_reponse_ok();
530 _ ->
531 erreur("Vous ne posséder pas ce troll")
532 end;
533 _ ->
534 erreur("Seul les ekMaster peuvent proposer des trolls")
535 end.
536
537
538 del_troll(
539 [
540 {cookie, Cookie},
541 {troll_id, Troll_id}
542 ]
543 ) ->
544 % controle que l'utilisateur est un admin
545 case euphorik_bd:user_by_cookie(Cookie) of
546 {ok, User = #user{ek_master = true}} ->
547 User_id = User#user.id,
548 case euphorik_bd:troll_by_id(Troll_id) of
549 {ok, #troll{id_user = User_id}} ->
550 euphorik_bd:del_troll(Troll_id),
551 json_reponse_ok();
552 _ ->
553 erreur("Vous ne posséder pas ce troll")
554 end;
555 _ ->
556 erreur("Seul les ekMaster peuvent proposer des trolls")
557 end.
558
559
560 unban_ip(
561 [
562 {cookie, Cookie},
563 {ip, IP}
564 ]
565 ) ->
566 case euphorik_bd:user_by_cookie(Cookie) of
567 {ok, #user{ek_master = true}} ->
568 euphorik_bd:deban(euphorik_common:unserialize_ip(IP)),
569 json_reponse_ok();
570 _ ->
571 erreur("Seul les ekMaster peuvent connaitre la liste des ips bannies")
572 end.
573
574
575 list_banned_ips(
576 [
577 {cookie, Cookie}
578 ]
579 ) ->
580 case euphorik_bd:user_by_cookie(Cookie) of
581 {ok, #user{ek_master = true}} ->
582 {
583 struct,
584 [
585 {reply, "list_banned_ips"},
586 {list, {array, lists:map(
587 fun({IP, T, Users}) ->
588 {struct,
589 [
590 {ip, euphorik_common:serialize_ip(IP)},
591 {remaining_time, format_minutes(T)},
592 {users, {array, lists:map(
593 fun({Pseudo, Login}) ->
594 {struct,
595 [
596 {nick, Pseudo},
597 {login, Login}
598 ]
599 }
600 end,
601 Users
602 )}}
603 ]
604 }
605 end,
606 euphorik_bd:list_ban()
607 )}}
608 ]
609 };
610 _ ->
611 erreur("Seul les ekMaster peuvent connaitre la liste des ips bannies")
612 end.
613
614
615 % Construit une erreur
616 erreur(Message) ->
617 {
618 struct, [
619 {reply, "error"},
620 {error_message, Message}
621 ]
622 }.
623
624
625 % Formatage de minutes.
626 % par exemple : "1min", "45min", "1h23min", "1jour 2h34min"
627 format_minutes(Min) ->
628 Jours = Min div (60 * 24),
629 Heures = Min rem (60 * 24) div 60,
630 Minutes = Min rem (60),
631 if Jours =/= 0 -> integer_to_list(Jours) ++ " Jour" ++ if Jours > 1 -> "s"; true -> "" end ++ " "; true -> "" end ++
632 if Heures =/= 0 -> integer_to_list(Heures) ++ " heure" ++ if Heures > 1 -> "s"; true -> "" end; true -> "" end ++
633 if Minutes == 0 ->
634 "";
635 true ->
636 " " ++ integer_to_list(Minutes) ++ " minute" ++ if Minutes > 1 -> "s"; true -> "" end
637 end.
638
639
640 % Formatage d'une heure
641 % local_time() -> string
642 format_date(Date) ->
643 DateLocal = calendar:now_to_local_time(Date),
644 DateNowLocal = calendar:local_time(),
645 {{Annee, Mois, Jour}, {Heure, Minute, Seconde}} = DateLocal,
646 {{AnneeNow, _, _}, {_, _, _}} = DateNowLocal,
647 Hier = calendar:date_to_gregorian_days(element(1, DateLocal)) =:= calendar:date_to_gregorian_days(element(1, DateNowLocal)) - 1,
648 lists:flatten(
649 if element(1, DateLocal) =:= element(1, DateNowLocal) ->
650 "";
651 Hier ->
652 "Hier ";
653 Annee =:= AnneeNow ->
654 io_lib:format("~2.10.0B/~2.10.0B ", [Jour, Mois]);
655 true ->
656 io_lib:format("~2.10.0B/~2.10.0B/~B ", [Jour, Mois, Annee])
657 end ++
658 io_lib:format("~2.10.0B:~2.10.0B:~2.10.0B", [Heure, Minute, Seconde])
659 ).
660
661
662 json_reponse_ok() ->
663 {struct, [{reply, "ok"}]}.
664
665
666 json_reponse_login_ok(User) ->
667 {
668 struct, [
669 {reply, "login"},
670 {status, if (User#user.password =/= []) and (User#user.login =/= []) -> "auth_registered"; true -> "auth_not_registered" end},
671 {cookie, User#user.cookie},
672 {id, User#user.id},
673 {nick, User#user.pseudo},
674 {login, User#user.login},
675 {email, User#user.email},
676 {css, User#user.css},
677 {chat_order, atom_to_list(User#user.chat_order)},
678 {nick_format, atom_to_list(User#user.nick_format)},
679 {view_times, User#user.view_times},
680 {view_tooltips, User#user.view_tooltips},
681 {conversations, {array, lists:map(
682 fun({Racine, Reduit}) ->
683 {struct, [{root, Racine}, {minimized, Reduit}]}
684 end,
685 User#user.conversations
686 )}},
687 {ek_master, User#user.ek_master},
688 {ostentatious_master, atom_to_list(User#user.ostentatious_master)}
689 ]
690 }.
691
692 % Renvoie le message formaté en JSON.
693 % Mess est de type #minichat
694 % Repond_a est une liste d'id des messages auquel répond Mess
695 % User est l'utilisateur courant de type #user
696 json_message(Mess, Repond_a, User) ->
697 Est_proprietaire = User =/= inconnu andalso User#user.id =:= Mess#minichat.auteur_id,
698 A_repondu_a_message = User =/= inconnu andalso euphorik_bd:a_repondu_a_message(User#user.id, Mess#minichat.id),
699 Est_une_reponse_a_user = User =/= inconnu andalso euphorik_bd:est_une_reponse_a_user(User#user.id, Mess#minichat.id),
700 {ok, User_mess } = euphorik_bd:user_by_id(Mess#minichat.auteur_id),
701 {struct, [
702 {id, Mess#minichat.id},
703 {user_id, User_mess#user.id},
704 {date, format_date(Mess#minichat.date)},
705 {system, Mess#minichat.auteur_id =:= 0},
706 {owner, Est_proprietaire},
707 {answered, A_repondu_a_message},
708 {is_a_reply, Est_une_reponse_a_user},
709 {nick, Mess#minichat.pseudo},
710 {login, User_mess#user.login},
711 {content, Mess#minichat.contenu},
712 {root, Mess#minichat.racine_id},
713 {answer_to, {array, lists:map(
714 fun(Id_mess) ->
715 {ok, M} = euphorik_bd:message_by_id(Id_mess),
716 {ok, User_reponse} = euphorik_bd:user_by_mess(M#minichat.id),
717 {struct, [{id, M#minichat.id}, {nick, M#minichat.pseudo}, {login, User_reponse#user.login}]}
718 end,
719 Repond_a
720 )}},
721 {ek_master, User_mess#user.ek_master},
722 {ostentatious_master, atom_to_list(User_mess#user.ostentatious_master)}
723 ]}.