Mercurial > libervia-web
comparison src/browser/sat_browser/json.py @ 589:a5019e62c3e9 frontends_multi_profiles
browser side: big refactoring to base Libervia on QuickFrontend, first draft:
/!\ not finished, partially working and highly instable
- add collections module with an OrderedDict like class
- SatWebFrontend inherit from QuickApp
- general sat_frontends tools.jid module is used
- bridge/json methods have moved to json module
- UniBox is partially removed (should be totally removed before merge to trunk)
- Signals are now register with the generic registerSignal method (which is called mainly in QuickFrontend)
- the generic getOrCreateWidget method from QuickWidgetsManager is used instead of Libervia's specific methods
- all Widget are now based more or less directly on QuickWidget
- with the new QuickWidgetsManager.getWidgets method, it's no more necessary to check all widgets which are instance of a particular class
- ChatPanel and related moved to chat module
- MicroblogPanel and related moved to blog module
- global and overcomplicated send method has been disabled: each class should manage its own sending
- for consistency with other frontends, former ContactPanel has been renamed to ContactList and vice versa
- for the same reason, ChatPanel has been renamed to Chat
- for compatibility with QuickFrontend, a fake profile is used in several places, it is set to C.PROF_KEY_NONE (real profile is managed server side for obvious security reasons)
- changed default url for web panel to SàT website, and contact address to generic SàT contact address
- ContactList is based on QuickContactList, UI changes are done in update method
- bride call (now json module) have been greatly improved, in particular call can be done in the same way as for other frontends (bridge.method_name(arg1, arg2, ..., callback=cb, errback=eb). Blocking method must be called like async methods due to javascript architecture
- in bridge calls, a callback can now exists without errback
- hard reload on BridgeSignals remote error has been disabled, a better option should be implemented
- use of constants where that make sens, some style improvments
- avatars are temporarily disabled
- lot of code disabled, will be fixed or removed before merge
- various other changes, check diff for more details
server side: manage remote exception on getEntityData, removed getProfileJid call, added getWaitingConf, added getRoomsSubjects
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 24 Jan 2015 01:45:39 +0100 |
parents | src/browser/libervia_main.py@0a06cf833f5a |
children | 917e271975d9 |
comparison
equal
deleted
inserted
replaced
585:bade589dbd5a | 589:a5019e62c3e9 |
---|---|
1 #!/usr/bin/python | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # Libervia: a Salut à Toi frontend | |
5 # Copyright (C) 2011, 2012, 2013, 2014 Jérôme Poisson <goffi@goffi.org> | |
6 | |
7 # This program is free software: you can redistribute it and/or modify | |
8 # it under the terms of the GNU Affero General Public License as published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (at your option) any later version. | |
11 | |
12 # This program is distributed in the hope that it will be useful, | |
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 # GNU Affero General Public License for more details. | |
16 | |
17 # You should have received a copy of the GNU Affero General Public License | |
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
19 | |
20 | |
21 ### logging configuration ### | |
22 from sat.core.log import getLogger | |
23 log = getLogger(__name__) | |
24 ### | |
25 | |
26 from pyjamas.Timer import Timer | |
27 from pyjamas import Window | |
28 from pyjamas import JSONService | |
29 | |
30 from sat_browser.constants import Const as C | |
31 | |
32 | |
33 class LiberviaMethodProxy(object): | |
34 """This class manage calling for one method""" | |
35 | |
36 def __init__(self, parent, method): | |
37 self._parent = parent | |
38 self._method = method | |
39 | |
40 def call(self, *args, **kwargs): | |
41 """Method called when self._method attribue is used in JSON_PROXY_PARENT | |
42 | |
43 This method manage callback/errback in kwargs, and profile(_key) removing | |
44 @param *args: positional arguments of self._method | |
45 @param **kwargs: keyword arguments of self._method | |
46 """ | |
47 callback=kwargs.pop('callback', None) | |
48 errback=kwargs.pop('errback', None) | |
49 | |
50 # as profile is linked to browser session and managed server side, we remove them | |
51 profile_removed = False | |
52 try: | |
53 kwargs['profile'] # FIXME: workaround for pyjamas bug: KeyError is not raised iwith del | |
54 del kwargs['profile'] | |
55 profile_removed = True | |
56 except KeyError: | |
57 pass | |
58 | |
59 try: | |
60 kwargs['profile_key'] # FIXME: workaround for pyjamas bug: KeyError is not raised iwith del | |
61 del kwargs['profile_key'] | |
62 profile_removed = True | |
63 except KeyError: | |
64 pass | |
65 | |
66 if not profile_removed and args: | |
67 # if profile was not in kwargs, there is most probably one in args | |
68 args = list(args) | |
69 assert isinstance(args[-1], basestring) # Detect when we want to remove a callback (or something else) instead of the profile | |
70 del args[-1] | |
71 | |
72 if kwargs: | |
73 # kwargs should be empty here, we don't manage keyword arguments on bridge calls | |
74 log.error("kwargs is not empty after treatment on method call: kwargs={}".format(kwargs)) | |
75 | |
76 id_ = self._parent.callMethod(self._method, args) | |
77 | |
78 # callback or errback are managed in parent LiberviaJsonProxy with call id | |
79 if callback is not None: | |
80 self._parent.cb[id_] = callback | |
81 if errback is not None: | |
82 self._parent.eb[id_] = errback | |
83 | |
84 | |
85 class LiberviaJsonProxy(JSONService.JSONService): | |
86 | |
87 def __init__(self, url, methods): | |
88 self._serviceURL = url | |
89 self.methods = methods | |
90 JSONService.JSONService.__init__(self, url, self) | |
91 self.cb = {} | |
92 self.eb = {} | |
93 self._registerMethods(methods) | |
94 | |
95 def _registerMethods(self, methods): | |
96 if methods: | |
97 for method in methods: | |
98 log.debug("Registering JSON method call [{}]".format(method)) | |
99 setattr(self, | |
100 method, | |
101 getattr(LiberviaMethodProxy(self, method), 'call') | |
102 ) | |
103 | |
104 def callMethod(self, method, params, handler = None): | |
105 ret = super(LiberviaJsonProxy, self).callMethod(method, params, handler) | |
106 return ret | |
107 | |
108 def call(self, method, cb, *args): | |
109 # FIXME: deprecated call method, must be removed once it's not used anymore | |
110 id_ = self.callMethod(method, args) | |
111 log.debug("call: method={} [id={}], args={}".format(method, id_, args)) | |
112 if cb: | |
113 if isinstance(cb, tuple): | |
114 if len(cb) != 2: | |
115 log.error("tuple syntax for bridge.call is (callback, errback), aborting") | |
116 return | |
117 if cb[0] is not None: | |
118 self.cb[id_] = cb[0] | |
119 self.eb[id_] = cb[1] | |
120 else: | |
121 self.cb[id_] = cb | |
122 | |
123 def onRemoteResponse(self, response, request_info): | |
124 try: | |
125 _cb = self.cb[request_info.id] | |
126 except KeyError: | |
127 pass | |
128 else: | |
129 # if isinstance(_cb, tuple): | |
130 # #we have arguments attached to the callback | |
131 # #we send them after the answer | |
132 # callback, args = _cb | |
133 # callback(response, *args) | |
134 # else: | |
135 # #No additional argument, we call directly the callback | |
136 _cb(response) | |
137 del self.cb[request_info.id] | |
138 | |
139 try: | |
140 del self.eb[request_info.id] | |
141 except KeyError: | |
142 pass | |
143 | |
144 def onRemoteError(self, code, errobj, request_info): | |
145 """def dump(obj): | |
146 print "\n\nDUMPING %s\n\n" % obj | |
147 for i in dir(obj): | |
148 print "%s: %s" % (i, getattr(obj,i))""" | |
149 try: | |
150 _eb = self.eb[request_info.id] | |
151 except KeyError: | |
152 if code != 0: | |
153 log.error("Internal server error") | |
154 """for o in code, error, request_info: | |
155 dump(o)""" | |
156 else: | |
157 if isinstance(errobj['message'], dict): | |
158 log.error("Error %s: %s" % (errobj['message']['faultCode'], errobj['message']['faultString'])) | |
159 else: | |
160 log.error("%s" % errobj['message']) | |
161 else: | |
162 _eb((code, errobj)) | |
163 del self.eb[request_info.id] | |
164 | |
165 try: | |
166 del self.cb[request_info.id] | |
167 except KeyError: | |
168 pass | |
169 | |
170 | |
171 class RegisterCall(LiberviaJsonProxy): | |
172 def __init__(self): | |
173 LiberviaJsonProxy.__init__(self, "/register_api", | |
174 ["isRegistered", "isConnected", "asyncConnect", "registerParams", "getMenus"]) | |
175 | |
176 | |
177 class BridgeCall(LiberviaJsonProxy): | |
178 def __init__(self): | |
179 LiberviaJsonProxy.__init__(self, "/json_api", | |
180 ["getContacts", "addContact", "sendMessage", "sendMblog", "sendMblogComment", | |
181 "getLastMblogs", "getMassiveLastMblogs", "getMblogComments", | |
182 "getHistory", "getPresenceStatuses", "joinMUC", "mucLeave", "getRoomsJoined", | |
183 "getRoomsSubjects", "inviteMUC", "launchTarotGame", "getTarotCardsPaths", "tarotGameReady", | |
184 "tarotGamePlayCards", "launchRadioCollective", "getMblogs", "getMblogsWithComments", | |
185 "getWaitingSub", "subscription", "delContact", "updateContact", "getCard", | |
186 "getEntityData", "getParamsUI", "asyncGetParamA", "setParam", "launchAction", | |
187 "disconnect", "chatStateComposing", "getNewAccountDomain", "confirmationAnswer", | |
188 "syntaxConvert", "getAccountDialogUI", "getLastResource", "getWaitingConf", | |
189 ]) | |
190 def __call__(self, *args, **kwargs): | |
191 return LiberviaJsonProxy.__call__(self, *args, **kwargs) | |
192 | |
193 def getConfig(self, dummy1, dummy2): # FIXME | |
194 log.warning("getConfig is not implemeted in Libervia yet") | |
195 return '' | |
196 | |
197 def isConnected(self, dummy): # FIXME | |
198 log.warning("isConnected is not implemeted in Libervia as for now profile is connected if session is opened") | |
199 return True | |
200 | |
201 def getAvatarFile(self, hash_, callback=None): | |
202 log.warning("getAvatarFile only return hash in Libervia") | |
203 if callback is not None: | |
204 callback(hash_) | |
205 return hash_ | |
206 | |
207 | |
208 class BridgeSignals(LiberviaJsonProxy): | |
209 RETRY_BASE_DELAY = 1000 | |
210 | |
211 def __init__(self, host): | |
212 self.host = host | |
213 self.retry_delay = self.RETRY_BASE_DELAY | |
214 LiberviaJsonProxy.__init__(self, "/json_signal_api", | |
215 ["getSignals"]) | |
216 self._signals = {} # key: signal name, value: callback | |
217 | |
218 def onRemoteResponse(self, response, request_info): | |
219 if self.retry_delay != self.RETRY_BASE_DELAY: | |
220 log.info("Connection with server restablished") | |
221 self.retry_delay = self.RETRY_BASE_DELAY | |
222 LiberviaJsonProxy.onRemoteResponse(self, response, request_info) | |
223 | |
224 def onRemoteError(self, code, errobj, request_info): | |
225 if errobj['message'] == 'Empty Response': | |
226 Window.alert (u"Empty reponse bridgeSignal\ncode={}\nrequest_info: id={} method={} handler={}".format(code, request_info.id, request_info.method, request_info.handler)) | |
227 # FIXME: to check/replace by a proper session end on disconnected signal | |
228 # Window.getLocation().reload() # XXX: reset page in case of session ended. | |
229 # FIXME: Should be done more properly without hard reload | |
230 LiberviaJsonProxy.onRemoteError(self, code, errobj, request_info) | |
231 #we now try to reconnect | |
232 if isinstance(errobj['message'], dict) and errobj['message']['faultCode'] == 0: | |
233 Window.alert('You are not allowed to connect to server') | |
234 else: | |
235 def _timerCb(timer): | |
236 log.info("Trying to reconnect to server...") | |
237 self.getSignals(callback=self.signalHandler) | |
238 log.warning("Lost connection, trying to reconnect in {} s".format(self.retry_delay/1000)) | |
239 Timer(notify=_timerCb).schedule(self.retry_delay) | |
240 self.retry_delay *= 2 | |
241 | |
242 def register(self, name, callback, with_profile=True): | |
243 """Register a signal | |
244 | |
245 @param: name of the signal to register | |
246 @param callback: method to call | |
247 @param with_profile: True if the original bridge method need a profile | |
248 """ | |
249 log.debug("Registering signal {}".format(name)) | |
250 if name in self._signals: | |
251 log.error("Trying to register and already registered signal ({})".format(name)) | |
252 else: | |
253 self._signals[name] = (callback, with_profile) | |
254 | |
255 def signalHandler(self, signal_data): | |
256 self.getSignals(callback=self.signalHandler) | |
257 if len(signal_data) == 1: | |
258 signal_data.append([]) | |
259 log.debug("Got signal ==> name: %s, params: %s" % (signal_data[0], signal_data[1])) | |
260 name, args = signal_data | |
261 try: | |
262 callback, with_profile = self._signals[name] | |
263 except KeyError: | |
264 log.warning("Ignoring {} signal: no handler registered !".format(name)) | |
265 return | |
266 if with_profile: | |
267 args.append(C.PROF_KEY_NONE) | |
268 callback(*args) |