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)