FIX gros bug moisi (ajout de l'information du dernier message au niveau des conversat...
[euphorik.git] / modules / erl / euphorik_protocole.erl
1 % coding: utf-8
2 % Ce module gére les différents messages envoyés par le client (javascript) via AJAX.
3 % Les messages donnés ainsi que les réponses sont au format JSON.
4 % @author G.Burri
5
6 -module(euphorik_protocole).
7 -export([
8 register/1,
9 login/1,
10 logout/1,
11 profile/1,
12 wait_event/1,
13 put_message/1,
14 erreur/1
15 ]).
16
17 -include_lib("xmerl/include/xmerl.hrl").
18 -include("../include/euphorik_bd.hrl").
19 -include("../include/euphorik_defines.hrl").
20
21
22 % Une utilisateur s'enregistre avec un tuple {Login, Password}.
23 register([{login, Login}, {password, Password}]) ->
24 case euphorik_minichat:user_by_login(Login) of
25 {ok, _} ->
26 erreur("Login déjà existant");
27 _ ->
28 User = euphorik_minichat:nouveau_user(Login, Password, generer_cookie()),
29 json_reponse_login_ok(User)
30 end;
31 % Enregistrement sans {Login, Password}
32 register([]) ->
33 User = euphorik_minichat:nouveau_user("<nick>", generer_cookie()),
34 json_reponse_login_ok(User).
35
36
37 % Un utilisateur se logge (avec un couple {login, mot de passe})
38 login([{login, Login}, {password, Password}]) ->
39 loginUser(euphorik_minichat:user_by_login_password(Login, Password));
40 % Un utilisateur se logge (avec un cookie)
41 login([{cookie, Cookie}]) ->
42 loginUser(euphorik_minichat:user_by_cookie(Cookie)).
43
44 loginUser({ok, User}) ->
45 euphorik_minichat:update_date_derniere_connexion(User#user.id),
46 json_reponse_login_ok(User);
47 loginUser(_) ->
48 % ajoute un délais d'attente (TODO : un autre moyen plus élégant ?)
49 receive after 1000 ->
50 erreur("Erreur login")
51 end.
52
53
54 % Renvoie un string() représentant un cookie en base 36. Il y a 10^32 possibillités.
55 generer_cookie() ->
56 {A1,A2,A3} = now(),
57 random:seed(A1, A2, A3),
58 erlang:integer_to_list(random:uniform(math:pow(10, 32)), 36).
59
60
61 % Un utilisateur se délogge.
62 logout(_) ->
63 do_nothing.
64
65
66 % Modification du profile.
67 profile(
68 [
69 {cookie, Cookie},
70 {login, Login},
71 {password, Password},
72 {nick, Pseudo},
73 {email, Email},
74 {css, Css},
75 {nick_format, Nick_format_str},
76 {main_page, Main_page},
77 {conversations, {array, Conversations_json}}
78 ]
79 ) ->
80 % est-ce que les messages auquel on répond existent ?
81 Conversations = lists:foldr(
82 fun({struct, [{root, Root}, {page, Page}]}, Acc) ->
83 Message_existe = euphorik_minichat:message_existe(Root),
84 if Message_existe ->
85 [{Root, Page} | Acc];
86 true ->
87 Acc
88 end
89 end,
90 [],
91 Conversations_json
92 ),
93 case euphorik_minichat:set_profile(Cookie, Login, Password, Pseudo, Email, Css, list_to_atom(Nick_format_str), Main_page, Conversations) of
94 ok ->
95 json_reponse_ok();
96 login_deja_pris ->
97 erreur("Login déjà pris");
98 _ ->
99 erreur("Impossible de mettre à jour le profile")
100 end.
101
102
103 % Renvoie les messages appropriés.
104 % last_message id et cookie sont facultatifs
105 % TODO : erreur : {badmatch,false}
106 wait_event(Data) ->
107 Cookie = case lists:keysearch(cookie, 1, Data) of {value, {_, C}} -> C; _ -> inconnu end,
108 Last_message_id = case lists:keysearch(last_message_id, 1, Data) of {value, {_, Id}} -> Id; _ -> 0 end,
109 {value, {_, Message_count}} = lists:keysearch(message_count, 1, Data),
110 Main_page = case lists:keysearch(main_page, 1, Data) of {value, {_, P}} -> P; _ -> 1 end,
111 {value, {_, {array, Conversations_json}}} = lists:keysearch(conversations, 1, Data),
112 Conversations = lists:map(
113 fun({struct, [{root, Racine}, {page, Page} | Reste]}) ->
114 Last_mess_conv = case Reste of [{last_message_id, L}] -> L; _ -> 0 end,
115 {Racine, Page, Last_mess_conv}
116 end,
117 Conversations_json
118 ),
119 User = case euphorik_minichat:user_by_cookie(Cookie) of
120 {ok, U} -> U;
121 _ -> inconnu
122 end,
123 {struct, [
124 {reply, "new_message"},
125 {conversations, {array,
126 % accrochez-vous ca va siouxer ;)
127 lists:map(
128 fun({Conv, Plus}) ->
129 {struct, [
130 {last_page, not Plus},
131 {messages, {array,
132 lists:map(
133 fun({Mess, Repond_a}) ->
134 Est_proprietaire = User =/= inconnu andalso User#user.id =:= Mess#minichat.auteur_id,
135 A_repondu_a_message = User =/= inconnu andalso euphorik_minichat:a_repondu_a_message(User#user.id, Mess#minichat.id),
136 Est_une_reponse_a_user = User =/= inconnu andalso euphorik_minichat:est_une_reponse_a_user(User#user.id, Mess#minichat.id),
137 % io:format("Repond_a : ~p~n", [Repond_a]),
138 User_mess =
139 if Mess#minichat.auteur_id =:= 0 ->
140 inconnu;
141 true ->
142 {ok, U2} = euphorik_minichat:user_by_id(Mess#minichat.auteur_id),
143 U2
144 end,
145 {struct, [
146 {id, Mess#minichat.id},
147 {date, format_date(Mess#minichat.date)},
148 {system, Mess#minichat.auteur_id =:= 0},
149 {owner, Est_proprietaire},
150 {answered, A_repondu_a_message},
151 {is_a_reply, Est_une_reponse_a_user},
152 {nick, Mess#minichat.pseudo},
153 {login, if User_mess =:= inconnu -> Mess#minichat.pseudo; true -> User_mess#user.login end},
154 {content, Mess#minichat.contenu},
155 {answer_to, {array, lists:map(
156 fun(Id_mess) ->
157 {ok, M} = euphorik_minichat:message_by_id(Id_mess),
158 {ok, User_reponse} = euphorik_minichat:user_by_mess(M#minichat.id),
159 {struct, [{id, M#minichat.id}, {nick, M#minichat.pseudo}, {login, User_reponse#user.login}]}
160 end,
161 Repond_a
162 )}}
163 ]}
164 end,
165 Conv
166 )
167 }}
168 ]}
169 end,
170 euphorik_minichat_conversation:conversations(
171 Conversations,
172 Message_count,
173 Last_message_id,
174 Main_page
175 )
176 )
177 }}
178 ]}.
179
180
181 % Un utilisateur envoie un message
182 put_message(
183 [
184 {cookie, Cookie},
185 {nick, Nick},
186 {content, Content},
187 {answer_to, {array, Answer_to}}
188 ]
189 ) ->
190 case euphorik_minichat:user_by_cookie(Cookie) of
191 {ok, User} ->
192 Strip_content = string:strip(Content),
193 if (Strip_content =:= []) ->
194 erreur("Message vide");
195 true ->
196 % TODO : non-atomique (update_pseudo+nouveau_message)
197 euphorik_minichat:update_pseudo_user(User#user.id, Nick),
198 case euphorik_minichat:nouveau_message(Strip_content, User#user.id, Answer_to) of
199 erreur -> erreur("Impossible d'ajouter un nouveau message");
200 _ ->
201 json_reponse_ok()
202 end
203 end;
204 _ ->
205 erreur("Utilisateur inconnu")
206 end.
207
208
209 % Construit une erreur
210 erreur(Message) ->
211 {
212 struct, [
213 {reply, "error"},
214 {error_message, Message}
215 ]
216 }.
217
218
219 % Formatage d'une heure
220 % local_time() -> string
221 format_date(Date) ->
222 DateLocal = calendar:now_to_local_time(Date),
223 DateNowLocal = calendar:local_time(),
224 {{Annee, Mois, Jour}, {Heure, Minute, Seconde}} = DateLocal,
225 {{AnneeNow, _, _}, {_, _, _}} = DateNowLocal,
226 Hier = calendar:date_to_gregorian_days(element(1, DateLocal)) =:= calendar:date_to_gregorian_days(element(1, DateNowLocal)) - 1,
227 lists:flatten(
228 if element(1, DateLocal) =:= element(1, DateNowLocal) ->
229 "";
230 Hier ->
231 "Hier ";
232 Annee =:= AnneeNow ->
233 io_lib:format("~2.10.0B/~2.10.0B ", [Jour, Mois]);
234 true ->
235 io_lib:format("~2.10.0B/~2.10.0B/~B ", [Jour, Mois, Annee])
236 end ++
237 io_lib:format("~2.10.0B:~2.10.0B:~2.10.0B", [Heure, Minute, Seconde])
238 ).
239
240
241 %%%%%%%%% <Réponses JSON> %%%%%%%%%
242
243 json_reponse_ok() ->
244 {struct, [{reply, "ok"}]}.
245
246
247 json_reponse_login_ok(User) ->
248 {
249 struct, [
250 {reply, "login"},
251 {status, if (User#user.password =/= []) and (User#user.login =/= []) -> "auth_registered"; true -> "auth_not_registered" end},
252 {cookie, User#user.cookie},
253 {id, User#user.id},
254 {nick, User#user.pseudo},
255 {login, User#user.login},
256 {email, User#user.email},
257 {css, User#user.css},
258 {nick_format, atom_to_list(User#user.nick_format)},
259 {main_page, User#user.page_principale},
260 {conversations,
261 {array,
262 lists:map(
263 fun(C) ->
264 {struct,
265 [
266 {root, element(1, C)},
267 {page, element(2, C)}
268 ]
269 }
270 end,
271 User#user.conversations
272 )
273 }
274 }
275 ]
276 }.
277
278
279 %%%%%%%%% </réponses JSON> %%%%%%%%%