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