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