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