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