MOD Avancement sur le passage à JSON
[euphorik.git] / modules / erl / euphorik_protocole.erl
1 % coding: utf-8
2 % Ce module gére les différents message envoyé par le client (javascript) via AJAX.
3 % Par exemple le client peut demander les derniers messages du minichat.
4 % Les messages sont au format XML, la plus part des fonctions accepte un xmlDocument() et renvoie un string()
5 % qui est la réponse XML.
6 % Example XML : http://www.erlang.org/doc/apps/xmerl/xmerl_ug.html.
7 % @author G.Burri
8
9 -module(euphorik_protocole).
10 -export([
11 nouveau_user_login/1,
12 login/1,
13 logout/1,
14 profile/1,
15 refreshMessage/1,
16 message/1
17 ]).
18
19 -include_lib("xmerl/include/xmerl.hrl").
20 -include("../include/euphorik_bd.hrl").
21 -include("../include/euphorik_defines.hrl").
22
23
24 % Une utilisateur s'enregistre avec un tuple {Login, Password}.
25 % @spec nouveau_user_login(xmerl:xmlElement()) -> string()
26 nouveau_user_login(Action) ->
27 {Login, Password, Login_deja_pris} = case {xmerl_xpath:string("login", Action), xmerl_xpath:string("password", Action)} of
28 {[#xmlElement{content = [#xmlText{value = L}]}], [#xmlElement{content = [#xmlText{value = P}]}]} ->
29 {L, P, case euphorik_minichat:user_by_login(L) of {ok, _} -> true; _ -> false end};
30 _ -> {[], [], false}
31 end,
32 simple_xml_to_string(
33 if Login_deja_pris->
34 xml_reponse_login_pas_ok("Login déjà pris");
35 true ->
36 Cookie = generer_cookie(),
37 User = euphorik_minichat:nouveau_user(Login, Password, Cookie),
38 xml_reponse_login_ok(User)
39 end
40 ).
41
42
43 % Un utilisateur se logge.
44 login([{login, Login}, {password, Password}]) ->
45 {ok, User} = euphorik_minichat:user_by_login_password(Login, Password),
46
47
48 login(Action) ->
49 case xmerl_xpath:string("cookie", Action) of
50 [#xmlElement{content = [#xmlText{value = Cookie}]}] ->
51 loginUser(euphorik_minichat:user_by_cookie(Cookie));
52 _ ->
53 case {xmerl_xpath:string("login", Action), xmerl_xpath:string("password", Action)} of
54 {[#xmlElement{content = [#xmlText{value = Login}]}], [#xmlElement{content = [#xmlText{value = Password}]}]} ->
55 loginUser(euphorik_minichat:user_by_login_password(Login, Password));
56 _ ->
57 simple_xml_to_string(xml_reponse_login_pas_ok("XML malformé"))
58 end
59 end.
60 loginUser({ok, User}) ->
61 euphorik_minichat:update_date_derniere_connexion(User#user.id),
62 simple_xml_to_string(xml_reponse_login_ok(User));
63 loginUser(_) ->
64 simple_xml_to_string(xml_reponse_login_pas_ok("Erreur de login")).
65
66
67 % Renvoie un string() représentant un cookie en base 36. Il y a 10^32 possibillités.
68 generer_cookie() ->
69 {A1,A2,A3} = now(),
70 random:seed(A1, A2, A3),
71 erlang:integer_to_list(random:uniform(math:pow(10, 32)), 36).
72
73
74 % Un utilisateur se délogge.
75 logout(_) ->
76 do_nothing.
77
78
79 % Modification du profile.
80 profile(Action) ->
81 simple_xml_to_string(
82 case xmerl_xpath:string("cookie", Action) of
83 [#xmlElement{content = [#xmlText{value = Cookie}]}] ->
84 Login = case xmerl_xpath:string("login", Action) of [#xmlElement{content = [#xmlText{value = L}]}] -> L; _ -> undefined end,
85 Password = case xmerl_xpath:string("password", Action) of [#xmlElement{content = [#xmlText{value = P}]}] -> P; _ -> undefined end,
86 Pseudo = case xmerl_xpath:string("pseudo", Action) of [#xmlElement{content = [#xmlText{value = P2}]}] -> P2; _ -> Login end,
87 Email = case xmerl_xpath:string("email", Action) of [#xmlElement{content = [#xmlText{value = E}]}] -> E; _ -> undefined end,
88 Css = case xmerl_xpath:string("css", Action) of [#xmlElement{content = [#xmlText{value = C}]}] -> C; _ -> undefined end,
89 Page_principale = case xmerl_xpath:string("pagePrincipale", Action) of [#xmlElement{content = [#xmlText{value = P3}]}] -> list_to_integer(P3); _ -> undefined end,
90 Conversations = lists:foldr(
91 fun(Conv, Acc) ->
92 [#xmlElement{content = [#xmlText{value = Id_racine_str}]}] = xmerl_xpath:string("racine", Conv),
93 [#xmlElement{content = [#xmlText{value = Page_conv_str}]}] = xmerl_xpath:string("page", Conv),
94 Message_id = erlang:list_to_integer(Id_racine_str, 36),
95 % vérification de la validité de l'id
96 Message_existe = euphorik_minichat:message_existe(Message_id),
97 if Message_existe ->
98 [{Message_id, list_to_integer(Page_conv_str)} | Acc];
99 true ->
100 Acc
101 end
102 end,
103 [],
104 xmerl_xpath:string("conversation", Action)
105 ),
106 case euphorik_minichat:set_profile(Cookie, Login, Password, Pseudo, Email, Css, Page_principale, Conversations) of
107 ok ->
108 xml_reponse_profile_ok();
109 login_deja_pris ->
110 xml_reponse_profile_pas_ok("Login déjà pris");
111 _ ->
112 xml_reponse_profile_pas_ok("Impossible de mettre à jour le profile")
113 end;
114 _ ->
115 xml_reponse_profile_pas_ok("XML malformé")
116 end
117 ).
118
119
120 % Renvoie les messages appropriés.
121 refreshMessage(Action) ->
122 simple_xml_to_string(
123 case xmerl_xpath:string("nombreMessage", Action) of % le nombre de message qu'affiche le client
124 [#xmlElement{content = [#xmlText{value = Nb_message_str}]}] ->
125 Nb_message = list_to_integer(Nb_message_str),
126 Dernier_id = case xmerl_xpath:string("dernierMessageId", Action) of % l'id du dernier message que connait le client
127 [#xmlElement{content = [#xmlText{value = D}]}] -> erlang:list_to_integer(D, 36);
128 _ -> 0
129 end,
130 User = case xmerl_xpath:string("cookie", Action) of
131 [#xmlElement{content = [#xmlText{value = Cookie}]}] ->
132 case euphorik_minichat:user_by_cookie(Cookie) of
133 {ok, U} -> U;
134 _ -> inconnu
135 end;
136 _ -> inconnu
137 end,
138 % accrochez-vous ca va siouxer ;)
139 [{reponse, [{name, "refreshMessages"}],
140 lists:map(
141 fun({Conv, Plus}) ->
142 {conversation, [],
143 [{autresPages, [], [atom_to_list(Plus)]} |
144 lists:map(
145 fun({Mess, Repond_a}) ->
146 Est_proprietaire = User =/= inconnu andalso User#user.id =:= Mess#minichat.auteur_id,
147 A_repondu_a_message = User =/= inconnu andalso euphorik_minichat:a_repondu_a_message(User#user.id, Mess#minichat.id),
148 Est_une_reponse_a_user = User =/= inconnu andalso euphorik_minichat:est_une_reponse_a_user(User#user.id, Mess#minichat.id),
149 User_mess =
150 if Mess#minichat.auteur_id =:= 0 ->
151 inconnu;
152 true ->
153 {ok, U2} = euphorik_minichat:user_by_id(Mess#minichat.auteur_id),
154 U2
155 end,
156 {message, [{id, erlang:integer_to_list(Mess#minichat.id, 36)}],
157 [
158 {date, [], [format_date(Mess#minichat.date)]},
159 {systeme, [], [atom_to_list(Mess#minichat.auteur_id =:= 0)]},
160 {proprietaire, [], [atom_to_list(Est_proprietaire)]},
161 {repondu, [], [atom_to_list(A_repondu_a_message)]},
162 {reponse, [], [atom_to_list(Est_une_reponse_a_user)]},
163 {pseudo, [], [Mess#minichat.pseudo]},
164 {login, [], [if User_mess =:= inconnu -> Mess#minichat.pseudo; true -> User_mess#user.login end]},
165 {contenu, [], [Mess#minichat.contenu]},
166 {repondA, [], xml_repond_a(Repond_a)}
167 ]
168 }
169 end,
170 Conv
171 )
172 ]
173 }
174 end,
175 euphorik_minichat_conversation:conversations(
176 if User =/= inconnu -> User#user.conversations; true -> [] end,
177 Nb_message,
178 Dernier_id,
179 if User =/= inconnu -> User#user.page_principale; true -> 1 end
180 )
181 )
182 }];
183 _ ->
184 [{reponse, [{name, "refreshMessages"}], [{erreur, [], ["erreur"]}]}]
185 end
186 ).
187
188
189 % Prend une liste de xml text node et en resort un string()
190 % xmerl : "test & test" devient deux fragments de texte : "test " et "& test", il faut donc rassembler les morceaux...
191 defragmenter(Text_nodes) ->
192 lists:foldl(fun(Node, Acc) -> #xmlText{value = V} = Node, Acc ++ V end, [], Text_nodes).
193
194
195 % Un utilisateur envoie un message
196 message(Action) ->
197 simple_xml_to_string(
198 case {
199 xmerl_xpath:string("cookie", Action),
200 xmerl_xpath:string("pseudo", Action),
201 xmerl_xpath:string("contenu", Action)
202 } of
203 {
204 [#xmlElement{content = [#xmlText{value = Cookie}]}],
205 [#xmlElement{content = Pseudo_fragments}],
206 [#xmlElement{content = Contenu_fragments}]
207 } ->
208 case euphorik_minichat:user_by_cookie(Cookie) of
209 {ok, U} ->
210 Pseudo = defragmenter(Pseudo_fragments),
211 Contenu = defragmenter(Contenu_fragments),
212 % met à jour le pseudo du user
213 euphorik_minichat:update_pseudo_user(U#user.id, Pseudo),
214 Reponses = case xmerl_xpath:string("reponses", Action) of
215 [#xmlElement{content = C}] ->
216 lists:map(
217 fun (Reponse) ->
218 #xmlElement{attributes = [#xmlAttribute{name = id, value = Id_reponse}]} = Reponse,
219 erlang:list_to_integer(Id_reponse, 36)
220 end
221 , C);
222 _ -> []
223 end,
224 Contenu_strip = string:strip(Contenu),
225 if Contenu_strip =:= [] -> xml_reponse_message(pas_ok);
226 true ->
227 case euphorik_minichat:nouveau_message(Contenu, U#user.id, Reponses) of
228 erreur -> xml_reponse_message(pas_ok);
229 _ -> xml_reponse_message(ok)
230 end
231 end;
232 _ -> xml_reponse_message(pas_ok)
233 end;
234 _ ->
235 xml_reponse_message(pas_ok)
236 end
237 ).
238
239
240 % Formatage d'une heure
241 % local_time() -> string
242 format_date(Date) ->
243 DateLocal = calendar:now_to_local_time(Date),
244 DateNowLocal = calendar:local_time(),
245 {{Annee, Mois, Jour}, {Heure, Minute, Seconde}} = DateLocal,
246 {{AnneeNow, _, _}, {_, _, _}} = DateNowLocal,
247 Hier = calendar:date_to_gregorian_days(element(1, DateLocal)) =:= calendar:date_to_gregorian_days(element(1, DateNowLocal)) - 1,
248 if element(1, DateLocal) =:= element(1, DateNowLocal) ->
249 "";
250 Hier ->
251 "Hier ";
252 Annee =:= AnneeNow ->
253 io_lib:format("~2.10.0B/~2.10.0B ", [Jour, Mois]);
254 true ->
255 io_lib:format("~2.10.0B/~2.10.0B/~B ", [Jour, Mois, Annee])
256 end ++
257 io_lib:format("~2.10.0B:~2.10.0B:~2.10.0B", [Heure, Minute, Seconde]).
258
259
260 %%%%%%%%% <Réponses XML> %%%%%%%%%
261 simple_xml_to_string(XML) ->
262 lists:flatten(xmerl:export_simple(XML, xmerl_xml, [{prolog, ["<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n"]}])).
263
264
265 % Construit une réponse positive à un login
266 % si Enregistre vaut true alors cela veut dire que la personne s'est enregistré (elle possède au moins un login et un password)
267 xml_reponse_login_ok(User) ->
268 [{reponse, [{name, "login"}],
269 [
270 {statut, [if (User#user.password =/= []) and (User#user.login =/= []) -> "enregistre"; true -> "identifie" end]},
271 {cookie, [User#user.cookie]},
272 {id, [erlang:integer_to_list(User#user.id, 36)]},
273 {pseudo, [User#user.pseudo]},
274 {login, [User#user.login]},
275 {email, [User#user.email]},
276 {css, [User#user.css]},
277 {pagePrincipale, [integer_to_list(User#user.page_principale)]}
278 ] ++
279 lists:map(
280 fun(C) ->
281 {conversation,
282 [
283 {racine, [erlang:integer_to_list(element(1, C), 36)]},
284 {page, [integer_to_list(element(2, C))]}
285 ]
286 }
287 end,
288 User#user.conversations
289 )
290 }].
291
292
293 % Construit un réponse négative à un login
294 xml_reponse_login_pas_ok(Message) ->
295 [{reponse, [{name, "login"}],
296 [
297 {statut, ["erreur"]},
298 {information, [Message]}
299 ]
300 }].
301
302
303 xml_reponse_profile_ok() ->
304 [{reponse, [{name, "profile"}],
305 [
306 {statut, ["ok"]}
307 ]
308 }].
309
310
311 xml_reponse_profile_pas_ok(Message) ->
312 [{reponse, [{name, "profile"}],
313 [
314 {statut, ["pas ok"]},
315 {information, [Message]}
316 ]
317 }].
318
319
320 % Pas utilisé
321 %~ xml_conversation(Mess_id, Nb) ->
322 %~ {Mess_id, Conversation} = minichat:conversation(Mess_id, Nb),
323 %~ xml_conversation(Conversation).
324 %~ xml_conversation([]) -> [];
325 %~ xml_conversation(Liste_id) ->
326 %~ lists:map(
327 %~ fun({Id, Sous_liste}) ->
328 %~ {id, [{id, erlang:integer_to_list(Id, 36)}], xml_conversation(Sous_liste)}
329 %~ end,
330 %~ Liste_id
331 %~ ).
332
333
334 % Renvoie un element XML representant une liste de messages auquel le message M_id repond
335 xml_repond_a(Reponses) ->
336 lists:map(
337 fun(Id_mess) ->
338 {ok, M} = euphorik_minichat:message_by_id(Id_mess),
339 {ok, User} = euphorik_minichat:user_by_mess(Id_mess),
340 {id, [{id, erlang:integer_to_list(M#minichat.id, 36)}, {pseudo, M#minichat.pseudo}, {login, User#user.login}], []}
341 end,
342 Reponses
343 ).
344
345
346 xml_reponse_message(Ok) ->
347 [
348 {reponse, [{name, "message"}],
349 [
350 {statut, [], [case Ok of ok -> "ok"; pas_ok -> "pas ok" end]}
351 ]
352 }
353 ].
354
355 %%%%%%%%% </réponses XML> %%%%%%%%%