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