comparison src/browser/libervia_main.py @ 679:a90cc8fc9605

merged branch frontends_multi_profiles
author Goffi <goffi@goffi.org>
date Wed, 18 Mar 2015 16:15:18 +0100
parents 3eb3a2c0c011 849ffb24d5bf
children 801eb94aa869
comparison
equal deleted inserted replaced
590:1bffc4c244c3 679:a90cc8fc9605
15 # GNU Affero General Public License for more details. 15 # GNU Affero General Public License for more details.
16 16
17 # You should have received a copy of the GNU Affero General Public License 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/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 import pyjd # this is dummy in pyjs
21 20
22 ### logging configuration ### 21 ### logging configuration ###
23 from sat_browser import logging 22 from sat_browser import logging
24 logging.configure() 23 logging.configure()
25 from sat.core.log import getLogger 24 from sat.core.log import getLogger
26 log = getLogger(__name__) 25 log = getLogger(__name__)
27 ### 26 ###
28 27
28 from sat.core.i18n import D_
29
30 from sat_frontends.quick_frontend.quick_app import QuickApp
31 from sat_frontends.quick_frontend import quick_widgets
32 from sat_frontends.quick_frontend import quick_menus
33
29 from sat_frontends.tools.misc import InputHistory 34 from sat_frontends.tools.misc import InputHistory
30 from sat_frontends.tools import strings 35 from sat_frontends.tools import strings
36 from sat_frontends.tools import jid
37 from sat_frontends.tools import host_listener
31 from sat.core.i18n import _ 38 from sat.core.i18n import _
32 39
33 from pyjamas.ui.RootPanel import RootPanel 40 from pyjamas.ui.RootPanel import RootPanel
34 from pyjamas.ui.HTML import HTML 41 from pyjamas.ui.HTML import HTML
35 from pyjamas.ui.KeyboardListener import KEY_ESCAPE 42 from pyjamas.ui.KeyboardListener import KEY_ESCAPE
36 from pyjamas.Timer import Timer 43 from pyjamas.Timer import Timer
37 from pyjamas import Window, DOM 44 from pyjamas import Window, DOM
38 from pyjamas.JSONService import JSONProxy 45
39 46 from sat_browser import json
40 from sat_browser import register 47 from sat_browser import register
41 from sat_browser import contact 48 from sat_browser.contact_list import ContactList
42 from sat_browser import base_widget 49 from sat_browser import widget
43 from sat_browser import panels 50 from sat_browser import main_panel
51 from sat_browser import blog
44 from sat_browser import dialog 52 from sat_browser import dialog
45 from sat_browser import jid
46 from sat_browser import xmlui 53 from sat_browser import xmlui
47 from sat_browser import html_tools 54 from sat_browser import html_tools
48 from sat_browser import notification 55 from sat_browser import notification
56 from sat_browser import libervia_widget
49 57
50 from sat_browser.constants import Const as C 58 from sat_browser.constants import Const as C
59 import os.path
60
51 61
52 try: 62 try:
53 # FIXME: import plugin dynamically 63 # FIXME: import plugin dynamically
54 from sat_browser import plugin_sec_otr 64 from sat_browser import plugin_sec_otr
55 except ImportError: 65 except ImportError:
56 pass 66 pass
57 67
68
69 unicode = str # FIXME: pyjamas workaround
70
71
58 MAX_MBLOG_CACHE = 500 # Max microblog entries kept in memories 72 MAX_MBLOG_CACHE = 500 # Max microblog entries kept in memories
59 73
60 # Set to true to not create a new LiberviaWidget when a similar one 74 # Set to true to not create a new LiberviaWidget when a similar one
61 # already exist (i.e. a chat panel with the same target). Instead 75 # already exist (i.e. a chat panel with the same target). Instead
62 # the existing widget will be eventually removed from its parent 76 # the existing widget will be eventually removed from its parent
63 # and added to new base_widget.WidgetsPanel, or replaced to the expected 77 # and added to new libervia_widget.WidgetsPanel, or replaced to the expected
64 # position if the previous and the new parent are the same. 78 # position if the previous and the new parent are the same.
65 REUSE_EXISTING_LIBERVIA_WIDGETS = True 79 # REUSE_EXISTING_LIBERVIA_WIDGETS = True # FIXME
66 80
67 81
68 class LiberviaJsonProxy(JSONProxy): 82 class SatWebFrontend(InputHistory, QuickApp):
69 def __init__(self, *args, **kwargs):
70 JSONProxy.__init__(self, *args, **kwargs)
71 self.handler = self
72 self.cb = {}
73 self.eb = {}
74
75 def call(self, method, cb, *args):
76 _id = self.callMethod(method, args)
77 if cb:
78 if isinstance(cb, tuple):
79 if len(cb) != 2:
80 log.error("tuple syntax for bridge.call is (callback, errback), aborting")
81 return
82 if cb[0] is not None:
83 self.cb[_id] = cb[0]
84 self.eb[_id] = cb[1]
85 else:
86 self.cb[_id] = cb
87
88 def onRemoteResponse(self, response, request_info):
89 if request_info.id in self.cb:
90 _cb = self.cb[request_info.id]
91 # if isinstance(_cb, tuple):
92 # #we have arguments attached to the callback
93 # #we send them after the answer
94 # callback, args = _cb
95 # callback(response, *args)
96 # else:
97 # #No additional argument, we call directly the callback
98 _cb(response)
99 del self.cb[request_info.id]
100 if request_info.id in self.eb:
101 del self.eb[request_info.id]
102
103 def onRemoteError(self, code, errobj, request_info):
104 """def dump(obj):
105 print "\n\nDUMPING %s\n\n" % obj
106 for i in dir(obj):
107 print "%s: %s" % (i, getattr(obj,i))"""
108 if request_info.id in self.eb:
109 _eb = self.eb[request_info.id]
110 _eb((code, errobj))
111 del self.cb[request_info.id]
112 del self.eb[request_info.id]
113 else:
114 if code != 0:
115 log.error("Internal server error")
116 """for o in code, error, request_info:
117 dump(o)"""
118 else:
119 if isinstance(errobj['message'], dict):
120 log.error("Error %s: %s" % (errobj['message']['faultCode'], errobj['message']['faultString']))
121 else:
122 log.error("%s" % errobj['message'])
123
124
125 class RegisterCall(LiberviaJsonProxy):
126 def __init__(self):
127 LiberviaJsonProxy.__init__(self, "/register_api",
128 ["isRegistered", "isConnected", "asyncConnect", "registerParams", "getMenus"])
129
130
131 class BridgeCall(LiberviaJsonProxy):
132 def __init__(self):
133 LiberviaJsonProxy.__init__(self, "/json_api",
134 ["getContacts", "addContact", "sendMessage", "sendMblog", "sendMblogComment",
135 "getMblogs", "getMassiveMblogs", "getMblogComments", "getProfileJid",
136 "getHistory", "getPresenceStatuses", "joinMUC", "mucLeave", "getRoomsJoined",
137 "inviteMUC", "launchTarotGame", "getTarotCardsPaths", "tarotGameReady",
138 "tarotGamePlayCards", "launchRadioCollective", "getMblogs", "getMblogsWithComments",
139 "getWaitingSub", "subscription", "delContact", "updateContact", "getCard",
140 "getEntityData", "getParamsUI", "asyncGetParamA", "setParam", "launchAction",
141 "disconnect", "chatStateComposing", "getNewAccountDomain", "confirmationAnswer",
142 "syntaxConvert", "getAccountDialogUI", "getLastResource"
143 ])
144
145
146 class BridgeSignals(LiberviaJsonProxy):
147 RETRY_BASE_DELAY = 1000
148
149 def __init__(self, host):
150 self.host = host
151 self.retry_delay = self.RETRY_BASE_DELAY
152 LiberviaJsonProxy.__init__(self, "/json_signal_api",
153 ["getSignals"])
154
155 def onRemoteResponse(self, response, request_info):
156 self.retry_delay = self.RETRY_BASE_DELAY
157 LiberviaJsonProxy.onRemoteResponse(self, response, request_info)
158
159 def onRemoteError(self, code, errobj, request_info):
160 if errobj['message'] == 'Empty Response':
161 Window.getLocation().reload() # XXX: reset page in case of session ended.
162 # FIXME: Should be done more properly without hard reload
163 LiberviaJsonProxy.onRemoteError(self, code, errobj, request_info)
164 #we now try to reconnect
165 if isinstance(errobj['message'], dict) and errobj['message']['faultCode'] == 0:
166 Window.alert('You are not allowed to connect to server')
167 else:
168 def _timerCb(timer):
169 self.host.bridge_signals.call('getSignals', self.host._getSignalsCB)
170 Timer(notify=_timerCb).schedule(self.retry_delay)
171 self.retry_delay *= 2
172
173
174 class SatWebFrontend(InputHistory):
175 def onModuleLoad(self): 83 def onModuleLoad(self):
176 log.info("============ onModuleLoad ==============") 84 log.info("============ onModuleLoad ==============")
177 panels.ChatPanel.registerClass() 85 self.bridge_signals = json.BridgeSignals(self)
178 panels.MicroblogPanel.registerClass() 86 QuickApp.__init__(self, json.BridgeCall)
179 self.whoami = None 87 self.uni_box = None # FIXME: to be removed
180 self._selected_listeners = set() 88 self.panel = main_panel.MainPanel(self)
181 self.bridge = BridgeCall()
182 self.bridge_signals = BridgeSignals(self)
183 self.uni_box = None
184 self.status_panel = HTML('<br />')
185 self.contact_panel = contact.ContactPanel(self)
186 self.panel = panels.MainPanel(self)
187 self.discuss_panel = self.panel.discuss_panel
188 self.tab_panel = self.panel.tab_panel 89 self.tab_panel = self.panel.tab_panel
189 self.tab_panel.addTabListener(self) 90 self.tab_panel.addTabListener(self)
190 self.libervia_widgets = set() # keep track of all actives LiberviaWidgets
191 self.room_list = [] # list of rooms
192 self.mblog_cache = [] # used to keep our own blog entries in memory, to show them in new mblog panel
193 self.avatars_cache = {} # keep track of jid's avatar hash (key=jid, value=file)
194 self._register_box = None 91 self._register_box = None
195 RootPanel().add(self.panel) 92 RootPanel().add(self.panel)
93
196 self.notification = notification.Notification() 94 self.notification = notification.Notification()
197 DOM.addEventPreview(self) 95 DOM.addEventPreview(self)
198 self.importPlugins() 96 self.importPlugins()
199 self._register = RegisterCall() 97 self._register = json.RegisterCall()
200 self._register.call('getMenus', self.gotMenus) 98 self._register.call('getMenus', self.gotMenus)
201 self._register.call('registerParams', None) 99 self._register.call('registerParams', None)
202 self._register.call('isRegistered', self._isRegisteredCB) 100 self._register.call('isRegistered', self._isRegisteredCB)
203 self.initialised = False 101 self.initialised = False
204 self.init_cache = [] # used to cache events until initialisation is done 102 self.init_cache = [] # used to cache events until initialisation is done
205 self.cached_params = {} 103 self.cached_params = {}
206 self.next_rsm_index = 0 104 self.next_rsm_index = 0
207 105
106 #FIXME: microblog cache should be managed directly in blog module
107 self.mblog_cache = [] # used to keep our own blog entries in memory, to show them in new mblog panel
108
109 # self._selected_listeners = set() # FIXME: to be done with new listeners mechanism
110
111 @property
112 def whoami(self):
113 # XXX: works because Libervia is mono-profile
114 # if one day Libervia manage several profiles at once, this must be deleted
115 return self.profiles[C.PROF_KEY_NONE].whoami
116
117 @property
118 def contact_list(self):
119 return self.contact_lists[C.PROF_KEY_NONE]
120
121 @property
122 def visible_widgets(self):
123 widgets_panel = self.tab_panel.getCurrentPanel()
124 return [wid for wid in widgets_panel.widgets if isinstance(wid, quick_widgets.QuickWidget)]
125
126 @property
127 def base_location(self):
128 """Return absolute base url of this Libervia instance"""
129 url = Window.getLocation().getHref()
130 if url.endswith(C.LIBERVIA_MAIN_PAGE):
131 url = url[:-len(C.LIBERVIA_MAIN_PAGE)]
132 if url.endswith("/"):
133 url = url[:-1]
134 return url
135
136 def registerSignal(self, functionName, handler=None, iface="core", with_profile=True):
137 if handler is None:
138 callback = getattr(self, "{}{}".format(functionName, "Handler"))
139 else:
140 callback = handler
141
142 self.bridge_signals.register(functionName, callback, with_profile=with_profile)
143
208 def importPlugins(self): 144 def importPlugins(self):
209 self.plugins = {} 145 self.plugins = {}
210 inhibited_menus = []
211 # FIXME: plugins import should be dynamic and generic like in sat
212 try: 146 try:
213 self.plugins['otr'] = plugin_sec_otr.OTR(self) 147 self.plugins['otr'] = plugin_sec_otr.OTR(self)
214 except TypeError: # plugin_sec_otr has not been imported 148 except TypeError: # plugin_sec_otr has not been imported
215 inhibited_menus.append('OTR') 149 pass
216 150
217 class DummyPlugin(object): 151 # def addSelectedListener(self, callback):
218 def inhibitMenus(self): 152 # self._selected_listeners.add(callback)
219 return inhibited_menus
220
221 self.plugins['dummy_plugin'] = DummyPlugin()
222
223 def addSelectedListener(self, callback):
224 self._selected_listeners.add(callback)
225 153
226 def getSelected(self): 154 def getSelected(self):
227 wid = self.tab_panel.getCurrentPanel() 155 wid = self.tab_panel.getCurrentPanel()
228 if not isinstance(wid, base_widget.WidgetsPanel): 156 if not isinstance(wid, libervia_widget.WidgetsPanel):
229 log.error("Tab widget is not a base_widget.WidgetsPanel, can't get selected widget") 157 log.error("Tab widget is not a WidgetsPanel, can't get selected widget")
230 return None 158 return None
231 return wid.selected 159 return wid.selected
232 160
233 def setSelected(self, widget): 161 def setSelected(self, widget):
234 """Define the selected widget""" 162 """Define the selected widget"""
235 widgets_panel = self.tab_panel.getCurrentPanel() 163 widgets_panel = self.tab_panel.getCurrentPanel()
236 if not isinstance(widgets_panel, base_widget.WidgetsPanel): 164 if not isinstance(widgets_panel, libervia_widget.WidgetsPanel):
237 return 165 return
238 166
239 selected = widgets_panel.selected 167 selected = widgets_panel.selected
240 168
241 if selected == widget: 169 if selected == widget:
242 return 170 return
243 171
244 if selected: 172 if selected:
245 selected.removeStyleName('selected_widget') 173 selected.removeStyleName('selected_widget')
246 174
175 # FIXME: check that widget is in the current WidgetsPanel
247 widgets_panel.selected = widget 176 widgets_panel.selected = widget
177 self.selected_widget = widget
248 178
249 if widget: 179 if widget:
250 widgets_panel.selected.addStyleName('selected_widget') 180 widgets_panel.selected.addStyleName('selected_widget')
251 181
252 for callback in self._selected_listeners: 182 # FIXME:
253 callback(widget) 183 # for callback in self._selected_listeners:
184 # callback(widget)
254 185
255 def resize(self): 186 def resize(self):
256 """Resize elements""" 187 """Resize elements"""
257 Window.onResize() 188 Window.onResize()
258 189
259 def onBeforeTabSelected(self, sender, tab_index): 190 def onBeforeTabSelected(self, sender, tab_index):
260 return True 191 return True
261 192
262 def onTabSelected(self, sender, tab_index): 193 def onTabSelected(self, sender, tab_index):
263 selected = self.getSelected() 194 pass
264 for callback in self._selected_listeners: 195 # selected = self.getSelected()
265 callback(selected) 196 # FIXME:
197 # for callback in self._selected_listeners:
198 # callback(selected)
266 199
267 def onEventPreview(self, event): 200 def onEventPreview(self, event):
268 if event.type in ["keydown", "keypress", "keyup"] and event.keyCode == KEY_ESCAPE: 201 if event.type in ["keydown", "keypress", "keyup"] and event.keyCode == KEY_ESCAPE:
269 #needed to prevent request cancellation in Firefox 202 #needed to prevent request cancellation in Firefox
270 event.preventDefault() 203 event.preventDefault()
271 return True 204 return True
272 205
273 def getAvatar(self, jid_str): 206 # FIXME: must not call _entityDataUpdatedCb by itself
207 # should not get VCard, backend plugin must be fixed too
208 def getAvatarURL(self, jid_):
274 """Return avatar of a jid if in cache, else ask for it. 209 """Return avatar of a jid if in cache, else ask for it.
275 210
276 @param jid_str (str): JID of the contact 211 @param jid_ (jid.JID): JID of the contact
277 @return: the URL to the avatar (str) 212 @return: the URL to the avatar (unicode)
278 """ 213 """
279 def dataReceived(result): 214 assert isinstance(jid_, jid.JID)
280 if 'avatar' in result: 215 contact_list = self.contact_list # pyjamas issue: need a temporary variable to call a property's method
281 self._entityDataUpdatedCb(jid_str, 'avatar', result['avatar']) 216 avatar_hash = contact_list.getCache(jid_, 'avatar')
282 else: 217 if avatar_hash is None:
283 self.bridge.call("getCard", None, jid_str) 218 # we have no value for avatar_hash, so we request the vcard
284 219 self.bridge.getCard(unicode(jid_), profile=C.PROF_KEY_NONE)
285 def avatarError(error_data): 220 if not avatar_hash:
286 # The jid is maybe not in our roster, we ask for the VCard 221 return C.DEFAULT_AVATAR_URL
287 self.bridge.call("getCard", None, jid_str) 222 ret = os.path.join(C.AVATARS_DIR, avatar_hash)
288 223 return ret
289 if jid_str not in self.avatars_cache:
290 self.bridge.call('getEntityData', (dataReceived, avatarError), jid_str, ['avatar'])
291 self.avatars_cache[jid_str] = C.DEFAULT_AVATAR
292 return self.avatars_cache[jid_str]
293 224
294 def registerWidget(self, wid): 225 def registerWidget(self, wid):
295 log.debug("Registering %s" % wid.getDebugName()) 226 log.debug("Registering %s" % wid.getDebugName())
296 self.libervia_widgets.add(wid) 227 self.libervia_widgets.add(wid)
297 228
302 log.warning('trying to remove a non registered Widget: %s' % wid.getDebugName()) 233 log.warning('trying to remove a non registered Widget: %s' % wid.getDebugName())
303 234
304 def refresh(self): 235 def refresh(self):
305 """Refresh the general display.""" 236 """Refresh the general display."""
306 self.panel.refresh() 237 self.panel.refresh()
307 if self.getCachedParam(C.COMPOSITION_KEY, C.ENABLE_UNIBOX_PARAM) == 'true':
308 self.uni_box = self.panel.unibox_panel.unibox
309 else:
310 self.uni_box = None
311 for lib_wid in self.libervia_widgets: 238 for lib_wid in self.libervia_widgets:
312 lib_wid.refresh() 239 lib_wid.refresh()
313 self.resize() 240 self.resize()
314 241
315 def addTab(self, label, wid, select=True):
316 """Create a new tab and eventually add a widget in
317 @param label: label of the tab
318 @param wid: LiberviaWidget to add
319 @param select: True to select the added tab
320 """
321 widgets_panel = base_widget.WidgetsPanel(self)
322 self.tab_panel.add(widgets_panel, label)
323 widgets_panel.addWidget(wid)
324 if select:
325 self.tab_panel.selectTab(self.tab_panel.getWidgetCount() - 1)
326 return widgets_panel
327
328 def addWidget(self, wid, tab_index=None): 242 def addWidget(self, wid, tab_index=None):
329 """ Add a widget at the bottom of the current or specified tab 243 """ Add a widget at the bottom of the current or specified tab
244
330 @param wid: LiberviaWidget to add 245 @param wid: LiberviaWidget to add
331 @param tab_index: index of the tab to add the widget to""" 246 @param tab_index: index of the tab to add the widget to
247 """
332 if tab_index is None or tab_index < 0 or tab_index >= self.tab_panel.getWidgetCount(): 248 if tab_index is None or tab_index < 0 or tab_index >= self.tab_panel.getWidgetCount():
333 panel = self.tab_panel.getCurrentPanel() 249 panel = self.tab_panel.getCurrentPanel()
334 else: 250 else:
335 panel = self.tab_panel.tabBar.getTabWidget(tab_index) 251 panel = self.tab_panel.deck.getWidget(tab_index)
336 panel.addWidget(wid) 252 panel.addWidget(wid)
337 253
338 def displayNotification(self, title, body): 254 def displayNotification(self, title, body):
339 self.notification.notify(title, body) 255 self.notification.notify(title, body)
340 256
341 def gotMenus(self, menus): 257 def gotMenus(self, backend_menus):
342 """Put the menus data in cache and build the main menu bar 258 """Put the menus data in cache and build the main menu bar
343 259
344 @param menus (list[tuple]): menu data 260 @param backend_menus (list[tuple]): menu data from backend
345 """ 261 """
346 def process(menus, inhibited=None): 262 main_menu = self.panel.menu # most of global menu callbacks are in main_menu
347 for raw_menu in menus: 263
348 id_, type_, path, path_i18n = raw_menu 264 # Categories (with icons)
349 if inhibited and path[0] in inhibited: 265 self.menus.addCategory(C.MENU_GLOBAL, [D_(u"General")], extra={'icon': 'home'})
350 continue 266 self.menus.addCategory(C.MENU_GLOBAL, [D_(u"Contacts")], extra={'icon': 'social'})
351 menus_data = self.menus.setdefault(type_, []) 267 self.menus.addCategory(C.MENU_GLOBAL, [D_(u"Groups")], extra={'icon': 'social'})
352 menus_data.append((id_, path, path_i18n)) 268 self.menus.addCategory(C.MENU_GLOBAL, [D_(u"Games")], extra={'icon': 'games'})
353 269
354 self.menus = {} 270 # menus to have before backend menus
355 inhibited = set() 271 self.menus.addMenu(C.MENU_GLOBAL, (D_(u"Groups"), D_(u"Discussion")), callback=main_menu.onJoinRoom)
356 extras = [] 272
357 for plugin in self.plugins.values(): 273 # menus added by the backend/plugins (include other types than C.MENU_GLOBAL)
358 if hasattr(plugin, "inhibitMenus"): 274 self.menus.addMenus(backend_menus, top_extra={'icon': 'plugins'})
359 inhibited.update(plugin.inhibitMenus()) 275
360 if hasattr(plugin, "extraMenus"): 276 # menus to have under backend menus
361 extras.extend(plugin.extraMenus()) 277 self.menus.addMenu(C.MENU_GLOBAL, (D_(u"Contacts"), D_(u"Manage groups")), callback=main_menu.onManageContactGroups)
362 process(menus, inhibited) 278
363 process(extras) 279 # separator and right hand menus
364 self.panel.menu.createMenus() 280 self.menus.addMenuItem(C.MENU_GLOBAL, [], quick_menus.MenuSeparator())
281
282 self.menus.addMenu(C.MENU_GLOBAL, (D_(u"Help"), D_("Social contract")), top_extra={'icon': 'help'}, callback=main_menu.onSocialContract)
283 self.menus.addMenu(C.MENU_GLOBAL, (D_(u"Help"), D_("About")), callback=main_menu.onAbout)
284 self.menus.addMenu(C.MENU_GLOBAL, (D_(u"Settings"), D_("Account")), top_extra={'icon': 'settings'}, callback=main_menu.onAccount)
285 self.menus.addMenu(C.MENU_GLOBAL, (D_(u"Settings"), D_("Parameters")), callback=main_menu.onParameters)
286 # XXX: temporary, will change when a full profile will be managed in SàT
287 self.menus.addMenu(C.MENU_GLOBAL, (D_(u"Settings"), D_("Upload avatar")), callback=main_menu.onAvatarUpload)
288
289 # we call listener to have menu added by local classes/plugins
290 self.callListeners('gotMenus') # FIXME: to be done another way or moved to quick_app
291
292 # and finally the menus which must appear at the bottom
293 self.menus.addMenu(C.MENU_GLOBAL, (D_(u"General"), D_(u"Disconnect")), callback=main_menu.onDisconnect)
294
295 # we can now display all the menus
296 main_menu.update(C.MENU_GLOBAL)
365 297
366 def _isRegisteredCB(self, result): 298 def _isRegisteredCB(self, result):
367 registered, warning = result 299 registered, warning = result
368 if not registered: 300 if not registered:
369 self._register_box = register.RegisterBox(self.logged) 301 self._register_box = register.RegisterBox(self.logged)
384 def logged(self): 316 def logged(self):
385 if self._register_box: 317 if self._register_box:
386 self._register_box.hide() 318 self._register_box.hide()
387 del self._register_box # don't work if self._register_box is None 319 del self._register_box # don't work if self._register_box is None
388 320
389 # display the real presence status panel 321 # display the presence status panel and tab bar
390 self.panel.header.remove(self.status_panel) 322 self.presence_status_panel = main_panel.PresenceStatusPanel(self)
391 self.status_panel = panels.PresenceStatusPanel(self) 323 self.panel.addPresenceStatusPanel(self.presence_status_panel)
392 self.panel.header.add(self.status_panel) 324 self.panel.tab_panel.getTabBar().setVisible(True)
325
326 self.bridge_signals.call('getSignals', self.bridge_signals.signalHandler)
393 327
394 #it's time to fill the page 328 #it's time to fill the page
395 self.bridge.call('getContacts', self._getContactsCB) 329 # self.bridge.call('getContacts', self._getContactsCB)
396 self.bridge.call('getParamsUI', self._getParamsUICB) 330 # self.bridge.call('getParamsUI', self._getParamsUICB)
397 self.bridge_signals.call('getSignals', self._getSignalsCB) 331 # self.bridge_signals.call('getSignals', self._getSignalsCB)
398 #We want to know our own jid 332 # #We want to know our own jid
399 self.bridge.call('getProfileJid', self._getProfileJidCB) 333 # self.bridge.call('getProfileJid', self._getProfileJidCB)
400 334
401 def domain_cb(value): 335 def domain_cb(value):
402 self._defaultDomain = value 336 self._defaultDomain = value
403 log.info("new account domain: %s" % value) 337 log.info("new account domain: %s" % value)
404 338
405 def domain_eb(value): 339 def domain_eb(value):
406 self._defaultDomain = "libervia.org" 340 self._defaultDomain = "libervia.org"
407 341
408 self.bridge.call("getNewAccountDomain", (domain_cb, domain_eb)) 342 self.bridge.getNewAccountDomain(callback=domain_cb, errback=domain_eb)
409 self.discuss_panel.addWidget(panels.MicroblogPanel(self, [])) 343 self.plug_profiles([C.PROF_KEY_NONE]) # XXX: None was used intitially, but pyjamas bug when using variable arguments and None is the only arg.
410 344 # self.discuss_panel.addWidget(panel.MicroblogPanel(self, []))
411 # get cached params and refresh the display 345
412 def param_cb(cat, name, count): 346 # # get cached params and refresh the display
413 count[0] += 1 347 # def param_cb(cat, name, count):
414 refresh = count[0] == len(C.CACHED_PARAMS) 348 # count[0] += 1
415 return lambda value: self._paramUpdate(name, value, cat, refresh) 349 # refresh = count[0] == len(C.CACHED_PARAMS)
416 350 # return lambda value: self._paramUpdate(name, value, cat, refresh)
417 count = [0] # used to do something similar to DeferredList 351
418 for cat, name in C.CACHED_PARAMS: 352 # count = [0] # used to do something similar to DeferredList
419 self.bridge.call('asyncGetParamA', param_cb(cat, name, count), name, cat) 353 # for cat, name in C.CACHED_PARAMS:
354 # self.bridge.call('asyncGetParamA', param_cb(cat, name, count), name, cat)
355
356 def profilePlugged(self, dummy): # FIXME: to be called as a "profilePlugged" listener?
357 QuickApp.profilePlugged(self, dummy)
358
359 microblog_widget = self.displayWidget(blog.MicroblogPanel, ())
360 self.setSelected(microblog_widget)
361
362 # we fill the panels already here
363 for wid in self.widgets.getWidgets(blog.MicroblogPanel):
364 if wid.accept_all():
365 self.bridge.getMassiveMblogs('ALL', (), None, profile=C.PROF_KEY_NONE, callback=wid.massiveInsert)
366 else:
367 self.bridge.getMassiveMblogs('GROUP', list(wid.accepted_groups), None, profile=C.PROF_KEY_NONE, callback=wid.massiveInsert)
368
369 #we ask for our own microblogs:
370 self.loadOurMainEntries()
371
372 def addContactList(self, dummy):
373 contact_list = ContactList(self)
374 self.panel.addContactList(contact_list)
375
376 # FIXME: the contact list height has to be set manually the first time
377 self.resize()
378
379 return contact_list
380
381 def newWidget(self, wid):
382 log.debug("newWidget: {}".format(wid))
383 self.addWidget(wid)
384
385 def newMessageHandler(self, from_jid_s, msg, type_, to_jid_s, extra, profile=C.PROF_KEY_NONE):
386 if type_ == C.MESS_TYPE_HEADLINE:
387 from_jid = jid.JID(from_jid_s)
388 if from_jid.domain == self._defaultDomain:
389 # we display announcement from the server in a dialog for better visibility
390 try:
391 title = extra['subject']
392 except KeyError:
393 title = _('Announcement from %s') % from_jid
394 msg = strings.addURLToText(html_tools.XHTML2Text(msg))
395 dialog.InfoDialog(title, msg).show()
396 return
397 QuickApp.newMessageHandler(self, from_jid_s, msg, type_, to_jid_s, extra, profile)
398
399 def disconnectedHandler(self, profile):
400 QuickApp.disconnectedHandler(self, profile)
401 Window.getLocation().reload()
402
403 def setStatusOnline(self, online=True, show='', statuses={}, profile=C.PROF_KEY_NONE):
404 self.presence_status_panel.setPresence(show)
405 if statuses:
406 # FIXME: retrieve user language status or fallback to 'default'
407 self.presence_status_panel.setStatus(statuses.values()[0])
420 408
421 def _tryAutoConnect(self, skip_validation=False): 409 def _tryAutoConnect(self, skip_validation=False):
422 """This method retrieve the eventual URL parameters to auto-connect the user. 410 """This method retrieve the eventual URL parameters to auto-connect the user.
423 @param skip_validation: if True, set the form values but do not validate it 411 @param skip_validation: if True, set the form values but do not validate it
424 """ 412 """
445 ui = xmlui.create(self, xml_data=data['xmlui']) 433 ui = xmlui.create(self, xml_data=data['xmlui'])
446 ui.show() 434 ui.show()
447 elif "public_blog" in data: 435 elif "public_blog" in data:
448 # TODO: use the bare instead of node when all blogs can be retrieved 436 # TODO: use the bare instead of node when all blogs can be retrieved
449 node = jid.JID(data['public_blog']).node 437 node = jid.JID(data['public_blog']).node
450 self.addTab("%s's blog" % node, panels.WebPanel(self, "/blog/%s" % node)) 438 # FIXME: "/blog/{}" won't work with unicode nodes
439 self.displayWidget(widget.WebWidget, "/blog/{}".format(node), show_url=False, new_tab=_(u"{}'s blog").format(unicode(node)))
451 else: 440 else:
452 dialog.InfoDialog("Error", 441 dialog.InfoDialog("Error",
453 "Unmanaged action result", Width="400px").center() 442 "Unmanaged action result", Width="400px").center()
454 443
455 def _actionEb(self, err_data): 444 def _actionEb(self, err_data):
456 err_code, err_obj = err_data 445 err_code, err_obj = err_data
457 dialog.InfoDialog("Error", 446 dialog.InfoDialog("Error",
458 str(err_obj), Width="400px").center() 447 unicode(err_obj), Width="400px").center()
459 448
460 def launchAction(self, callback_id, data): 449 def launchAction(self, callback_id, data=None, callback=None, profile=C.PROF_KEY_NONE):
461 """ Launch a dynamic action 450 """ Launch a dynamic action
462 @param callback_id: id of the action to launch 451 @param callback_id: id of the action to launch
463 @param data: data needed only for certain actions 452 @param data: data needed only for certain actions
464 453
465 """ 454 """
466 if data is None: 455 if data is None:
467 data = {} 456 data = {}
468 self.bridge.call('launchAction', (self._actionCb, self._actionEb), callback_id, data) 457 self.bridge.launchAction(callback_id, data, profile=profile, callback=self._actionCb, errback=self._actionEb)
469 458
470 def _getContactsCB(self, contacts_data): 459 def _getContactsCB(self, contacts_data):
471 for contact_ in contacts_data: 460 for contact_ in contacts_data:
472 jid, attributes, groups = contact_ 461 jid, attributes, groups = contact_
473 self._newContactCb(jid, attributes, groups) 462 self._newContactCb(jid, attributes, groups)
474
475 def _getSignalsCB(self, signal_data):
476 self.bridge_signals.call('getSignals', self._getSignalsCB)
477 if len(signal_data) == 1:
478 signal_data.append([])
479 log.debug("Got signal ==> name: %s, params: %s" % (signal_data[0], signal_data[1]))
480 name, args = signal_data
481 if name == 'personalEvent':
482 self._personalEventCb(*args)
483 elif name == 'newMessage':
484 self._newMessageCb(*args)
485 elif name == 'presenceUpdate':
486 self._presenceUpdateCb(*args)
487 elif name == 'paramUpdate':
488 self._paramUpdate(*args)
489 elif name == 'roomJoined':
490 self._roomJoinedCb(*args)
491 elif name == 'roomLeft':
492 self._roomLeftCb(*args)
493 elif name == 'roomUserJoined':
494 self._roomUserJoinedCb(*args)
495 elif name == 'roomUserLeft':
496 self._roomUserLeftCb(*args)
497 elif name == 'roomUserChangedNick':
498 self._roomUserChangedNickCb(*args)
499 elif name == 'askConfirmation':
500 self._askConfirmation(*args)
501 elif name == 'newAlert':
502 self._newAlert(*args)
503 elif name == 'tarotGamePlayers':
504 self._tarotGameStartedCb(True, *args)
505 elif name == 'tarotGameStarted':
506 self._tarotGameStartedCb(False, *args)
507 elif name == 'tarotGameNew' or \
508 name == 'tarotGameChooseContrat' or \
509 name == 'tarotGameShowCards' or \
510 name == 'tarotGameInvalidCards' or \
511 name == 'tarotGameCardsPlayed' or \
512 name == 'tarotGameYourTurn' or \
513 name == 'tarotGameScore':
514 self._tarotGameGenericCb(name, args[0], args[1:])
515 elif name == 'radiocolPlayers':
516 self._radioColStartedCb(True, *args)
517 elif name == 'radiocolStarted':
518 self._radioColStartedCb(False, *args)
519 elif name == 'radiocolPreload':
520 self._radioColGenericCb(name, args[0], args[1:])
521 elif name == 'radiocolPlay':
522 self._radioColGenericCb(name, args[0], args[1:])
523 elif name == 'radiocolNoUpload':
524 self._radioColGenericCb(name, args[0], args[1:])
525 elif name == 'radiocolUploadOk':
526 self._radioColGenericCb(name, args[0], args[1:])
527 elif name == 'radiocolSongRejected':
528 self._radioColGenericCb(name, args[0], args[1:])
529 elif name == 'subscribe':
530 self._subscribeCb(*args)
531 elif name == 'contactDeleted':
532 self._contactDeletedCb(*args)
533 elif name == 'newContact':
534 self._newContactCb(*args)
535 elif name == 'entityDataUpdated':
536 self._entityDataUpdatedCb(*args)
537 elif name == 'chatStateReceived':
538 self._chatStateReceivedCb(*args)
539 463
540 def _getParamsUICB(self, xml_ui): 464 def _getParamsUICB(self, xml_ui):
541 """Hide the parameters item if there's nothing to display""" 465 """Hide the parameters item if there's nothing to display"""
542 if not xml_ui: 466 if not xml_ui:
543 self.panel.menu.removeItemParams() 467 self.panel.menu.removeItemParams()
556 continue 480 continue
557 if 'groups' in mblog: 481 if 'groups' in mblog:
558 _groups = set(mblog['groups'].split() if mblog['groups'] else []) 482 _groups = set(mblog['groups'].split() if mblog['groups'] else [])
559 else: 483 else:
560 _groups = None 484 _groups = None
561 mblog_entry = panels.MicroblogItem(mblog) 485 mblog_entry = blog.MicroblogItem(mblog)
562 cache.append((_groups, mblog_entry)) 486 cache.append((_groups, mblog_entry))
563 487
564 self.mblog_cache.extend(cache) 488 self.mblog_cache.extend(cache)
565 if len(self.mblog_cache) > MAX_MBLOG_CACHE: 489 if len(self.mblog_cache) > MAX_MBLOG_CACHE:
566 del self.mblog_cache[0:len(self.mblog_cache - MAX_MBLOG_CACHE)] 490 del self.mblog_cache[0:len(self.mblog_cache - MAX_MBLOG_CACHE)]
567 491
568 widget_list = [mblog_panel] if mblog_panel else self.libervia_widgets 492 widget_list = [mblog_panel] if mblog_panel else self.widgets.getWidgets(blog.MicroblogPanel)
569 for lib_wid in widget_list: 493
570 if isinstance(lib_wid, panels.MicroblogPanel): 494 for wid in widget_list:
571 self.fillMicroblogPanel(lib_wid, cache) 495 self.fillMicroblogPanel(wid, cache)
496
497 # FIXME
572 498
573 if self.initialised: 499 if self.initialised:
574 return 500 return
575 self.initialised = True # initialisation phase is finished here 501 self.initialised = True # initialisation phase is finished here
576 for event_data in self.init_cache: # so we have to send all the cached events 502 for event_data in self.init_cache: # so we have to send all the cached events
577 self._personalEventCb(*event_data) 503 self.personalEventHandler(*event_data)
578 del self.init_cache 504 del self.init_cache
579 505
580 def _getProfileJidCB(self, jid_s): 506 def _getProfileJidCB(self, jid_s):
581 self.whoami = jid.JID(jid_s) 507 # FIXME
582 #we can now ask our status 508 raise Exception("should not be here !")
583 self.bridge.call('getPresenceStatuses', self._getPresenceStatusesCb) 509 # self.whoami = jid.JID(jid_s)
584 #the rooms where we are 510 # #we can now ask our status
585 self.bridge.call('getRoomsJoined', self._getRoomsJoinedCb) 511 # self.bridge.call('getPresenceStatuses', self._getPresenceStatusesCb)
586 #and if there is any subscription request waiting for us 512 # #the rooms where we are
587 self.bridge.call('getWaitingSub', self._getWaitingSubCb) 513 # self.bridge.call('getRoomsJoined', self._getRoomsJoinedCb)
588 #we fill the panels already here 514 # #and if there is any subscription request waiting for us
589 for lib_wid in self.libervia_widgets: 515 # self.bridge.call('getWaitingSub', self._getWaitingSubCb)
590 if isinstance(lib_wid, panels.MicroblogPanel): 516 # #we fill the panels already here
591 if lib_wid.accept_all(): 517 # for lib_wid in self.libervia_widgets:
592 self.bridge.call('getMassiveMblogs', lib_wid.massiveInsert, 'ALL', []) 518 # if isinstance(lib_wid, panel.MicroblogPanel):
593 else: 519 # if lib_wid.accept_all():
594 self.bridge.call('getMassiveMblogs', lib_wid.massiveInsert, 'GROUP', lib_wid.accepted_groups) 520 # self.bridge.call('getMassiveLastMblogs', lib_wid.massiveInsert, 'ALL', [], 10)
595 521 # else:
596 #we ask for our own microblogs: 522 # self.bridge.call('getMassiveLastMblogs', lib_wid.massiveInsert, 'GROUP', lib_wid.accepted_groups, 10)
597 self.loadOurMainEntries() 523
598 524 # #we ask for our own microblogs:
599 # initialize plugins which waited for the connection to be done 525 # self.loadOurMainEntries()
600 for plugin in self.plugins.values(): 526
601 if hasattr(plugin, 'profileConnected'): 527 # # initialize plugins which waited for the connection to be done
602 plugin.profileConnected() 528 # for plugin in self.plugins.values():
529 # if hasattr(plugin, 'profileConnected'):
530 # plugin.profileConnected()
603 531
604 def loadOurMainEntries(self, index=0, mblog_panel=None): 532 def loadOurMainEntries(self, index=0, mblog_panel=None):
605 """Load a page of our own blogs from the cache or ask them to the 533 """Load a page of our own blogs from the cache or ask them to the
606 backend. Then fill the panels with them. 534 backend. Then fill the panels with them.
607 535
608 @param index (int): starting index of the blog page to retrieve. 536 @param index (int): starting index of the blog page to retrieve.
609 @param mblog_panel (MicroblogPanel): the panel to fill, or all if None. 537 @param mblog_panel (MicroblogPanel): the panel to fill, or all if None.
610 """ 538 """
611 delta = index - self.next_rsm_index 539 delta = index - self.next_rsm_index
612 if delta < 0: 540 if delta < 0:
613 assert(mblog_panel is not None) 541 assert mblog_panel is not None
614 self.fillMicroblogPanel(mblog_panel, self.mblog_cache[index:index + C.RSM_MAX_ITEMS]) 542 self.fillMicroblogPanel(mblog_panel, self.mblog_cache[index:index + C.RSM_MAX_ITEMS])
615 return 543 return
616 544
617 def cb(result): 545 def cb(result):
618 self._ownBlogsFills(result, mblog_panel) 546 self._ownBlogsFills(result, mblog_panel)
619 547
620 rsm = {'max': str(delta + C.RSM_MAX_ITEMS), 'index': str(self.next_rsm_index)} 548 rsm = {'max': str(delta + C.RSM_MAX_ITEMS), 'index': str(self.next_rsm_index)}
621 self.bridge.call('getMassiveMblogs', cb, 'JID', [self.whoami.bare], rsm) 549 self.bridge.getMassiveMblogs('JID', [unicode(self.whoami.bare)], rsm, callback=cb, profile=C.PROF_KEY_NONE)
622 self.next_rsm_index = index + C.RSM_MAX_ITEMS 550 self.next_rsm_index = index + C.RSM_MAX_ITEMS
623 551
624 ## Signals callbacks ## 552 ## Signals callbacks ##
625 553
626 def _personalEventCb(self, sender, event_type, data): 554 def personalEventHandler(self, sender, event_type, data):
555 # FIXME: move some code from here to QuickApp
627 if not self.initialised: 556 if not self.initialised:
628 self.init_cache.append((sender, event_type, data)) 557 self.init_cache.append((sender, event_type, data))
629 return 558 return
630 sender = jid.JID(sender).bare 559 sender = jid.JID(sender).bare
631 if event_type == "MICROBLOG": 560 if event_type == "MICROBLOG":
634 return 563 return
635 if 'groups' in data: 564 if 'groups' in data:
636 _groups = set(data['groups'].split() if data['groups'] else []) 565 _groups = set(data['groups'].split() if data['groups'] else [])
637 else: 566 else:
638 _groups = None 567 _groups = None
639 mblog_entry = panels.MicroblogItem(data) 568 mblog_entry = blog.MicroblogItem(data)
640 569
641 for lib_wid in self.libervia_widgets: 570 for wid in self.widgets.getWidgets(blog.MicroblogPanel):
642 if isinstance(lib_wid, panels.MicroblogPanel): 571 wid.addEntryIfAccepted(sender, _groups, mblog_entry)
643 self.addBlogEntry(lib_wid, sender, _groups, mblog_entry)
644 572
645 if sender == self.whoami.bare: 573 if sender == self.whoami.bare:
646 found = False 574 found = False
647 for index in xrange(0, len(self.mblog_cache)): 575 for index in xrange(0, len(self.mblog_cache)):
648 entry = self.mblog_cache[index] 576 entry = self.mblog_cache[index]
655 if not found: 583 if not found:
656 self.mblog_cache.append((_groups, mblog_entry)) 584 self.mblog_cache.append((_groups, mblog_entry))
657 if len(self.mblog_cache) > MAX_MBLOG_CACHE: 585 if len(self.mblog_cache) > MAX_MBLOG_CACHE:
658 del self.mblog_cache[0:len(self.mblog_cache - MAX_MBLOG_CACHE)] 586 del self.mblog_cache[0:len(self.mblog_cache - MAX_MBLOG_CACHE)]
659 elif event_type == 'MICROBLOG_DELETE': 587 elif event_type == 'MICROBLOG_DELETE':
660 for lib_wid in self.libervia_widgets: 588 for wid in self.widgets.getWidgets(blog.MicroblogPanel):
661 if isinstance(lib_wid, panels.MicroblogPanel): 589 wid.removeEntry(data['type'], data['id'])
662 lib_wid.removeEntry(data['type'], data['id'])
663 log.debug("%s %s %s" % (self.whoami.bare, sender, data['type'])) 590 log.debug("%s %s %s" % (self.whoami.bare, sender, data['type']))
664 591
665 if sender == self.whoami.bare and data['type'] == 'main_item': 592 if sender == self.whoami.bare and data['type'] == 'main_item':
666 for index in xrange(0, len(self.mblog_cache)): 593 for index in xrange(0, len(self.mblog_cache)):
667 entry = self.mblog_cache[index] 594 entry = self.mblog_cache[index]
668 if entry[1].id == data['id']: 595 if entry[1].id == data['id']:
669 self.mblog_cache.remove(entry) 596 self.mblog_cache.remove(entry)
670 break 597 break
671 598
672 def addBlogEntry(self, mblog_panel, sender, _groups, mblog_entry):
673 """Check if an entry can go in MicroblogPanel and add to it
674 @param mblog_panel: MicroblogPanel instance
675 @param sender: jid of the entry sender
676 @param _groups: groups which can receive this entry
677 @param mblog_entry: panels.MicroblogItem instance"""
678 if mblog_entry.type == "comment" or mblog_panel.isJidAccepted(sender) or (_groups == None and self.whoami and sender == self.whoami.bare) \
679 or (_groups and _groups.intersection(mblog_panel.accepted_groups)):
680 mblog_panel.addEntry(mblog_entry)
681
682 def fillMicroblogPanel(self, mblog_panel, mblogs): 599 def fillMicroblogPanel(self, mblog_panel, mblogs):
683 """Fill a microblog panel with entries in cache 600 """Fill a microblog panel with entries in cache
601
684 @param mblog_panel: MicroblogPanel instance 602 @param mblog_panel: MicroblogPanel instance
685 """ 603 """
686 #XXX: only our own entries are cached 604 #XXX: only our own entries are cached
687 for cache_entry in mblogs: 605 for cache_entry in mblogs:
688 _groups, mblog_entry = cache_entry 606 _groups, mblog_entry = cache_entry
689 self.addBlogEntry(mblog_panel, self.whoami.bare, *cache_entry) 607 mblog_panel.addEntryIfAccepted(self.whoami.bare, *cache_entry)
690 608
691 def getEntityMBlog(self, entity): 609 def getEntityMBlog(self, entity):
692 log.info("geting mblog for entity [%s]" % (entity,)) 610 log.info("geting mblog for entity [%s]" % (entity,))
693 for lib_wid in self.libervia_widgets: 611 for lib_wid in self.libervia_widgets:
694 if isinstance(lib_wid, panels.MicroblogPanel): 612 if isinstance(lib_wid, blog.MicroblogPanel):
695 if lib_wid.isJidAccepted(entity): 613 if lib_wid.isJidAccepted(entity):
696 self.bridge.call('getMassiveMblogs', lib_wid.massiveInsert, 'JID', [entity]) 614 self.bridge.call('getMassiveMblogs', lib_wid.massiveInsert, 'JID', [unicode(entity)])
697 615
698 def getLiberviaWidget(self, class_, entity, ignoreOtherTabs=True): 616 # def getLiberviaWidget(self, class_, entity, ignoreOtherTabs=True):
699 """Get the corresponding panel if it exists. 617 # """Get the corresponding panel if it exists.
700 @param class_ (class): class of the panel (ChatPanel, MicroblogPanel...) 618 # @param class_ (class): class of the panel (ChatPanel, MicroblogPanel...)
701 @param entity (dict): dictionnary to define the entity. 619 # @param entity (dict): dictionnary to define the entity.
702 @param ignoreOtherTabs (bool): if True, the widgets that are not 620 # @param ignoreOtherTabs (bool): if True, the widgets that are not
703 contained by the currently selected tab will be ignored 621 # contained by the currently selected tab will be ignored
704 @return: the existing widget that has been found or None.""" 622 # @return: the existing widget that has been found or None."""
705 selected_tab = self.tab_panel.getCurrentPanel() 623 # selected_tab = self.tab_panel.getCurrentPanel()
706 for lib_wid in self.libervia_widgets: 624 # for lib_wid in self.libervia_widgets:
707 parent = lib_wid.getWidgetsPanel(expect=False) 625 # parent = lib_wid.getWidgetsPanel(expect=False)
708 if parent is None or (ignoreOtherTabs and parent != selected_tab): 626 # if parent is None or (ignoreOtherTabs and parent != selected_tab):
709 # do not return a widget that is not in the currently selected tab 627 # # do not return a widget that is not in the currently selected tab
710 continue 628 # continue
711 if isinstance(lib_wid, class_): 629 # if isinstance(lib_wid, class_):
712 try: 630 # try:
713 if lib_wid.matchEntity(*(entity.values())): # XXX: passing **entity bugs! 631 # if lib_wid.matchEntity(*(entity.values())): # XXX: passing **entity bugs!
714 log.debug("existing widget found: %s" % lib_wid.getDebugName()) 632 # log.debug("existing widget found: %s" % lib_wid.getDebugName())
715 return lib_wid 633 # return lib_wid
716 except AttributeError as e: 634 # except AttributeError as e:
717 e.stack_list() 635 # e.stack_list()
718 return None 636 # return None
719 return None 637 # return None
720 638
721 def getOrCreateLiberviaWidget(self, class_, entity, select=True, new_tab=None): 639 def displayWidget(self, class_, target, dropped=False, new_tab=None, *args, **kwargs):
722 """Get the matching LiberviaWidget if it exists, or create a new one. 640 """Get or create a LiberviaWidget and select it. When the user dropped
723 @param class_ (class): class of the panel (ChatPanel, MicroblogPanel...) 641 something, a new widget is always created, otherwise we look for an
724 @param entity (dict): dictionnary to define the entity. 642 existing widget and re-use it if it's in the current tab.
725 @param select (bool): if True, select the widget that has been found or created 643
726 @param new_tab (str): if not None, a widget which is created is created in 644 @arg class_(class): see quick_widgets.getOrCreateWidget
727 a new tab. In that case new_tab is a unicode to label that new tab. 645 @arg target: see quick_widgets.getOrCreateWidget
728 If new_tab is not None and a widget is found, no tab is created. 646 @arg dropped(bool): if True, assume the widget has been dropped
729 @return: the newly created wigdet if REUSE_EXISTING_LIBERVIA_WIDGETS 647 @arg new_tab(unicode): if not None, it holds the name of a new tab to
730 is set to False or if the widget has not been found, the existing 648 open for the widget. If None, use the default behavior.
731 widget that has been found otherwise.""" 649 @param args(list): optional args to create a new instance of class_
732 lib_wid = None 650 @param kwargs(list): optional kwargs to create a new instance of class_
733 tab = None 651 @return: the widget
734 if REUSE_EXISTING_LIBERVIA_WIDGETS: 652 """
735 lib_wid = self.getLiberviaWidget(class_, entity, new_tab is None) 653 kwargs['profile'] = C.PROF_KEY_NONE
736 if lib_wid is None: # create a new widget 654
737 lib_wid = class_.createPanel(self, *(entity.values())) # XXX: passing **entity bugs! 655 if dropped:
738 if new_tab is None: 656 kwargs['on_new_widget'] = None
739 self.addWidget(lib_wid) 657 kwargs['on_existing_widget'] = C.WIDGET_RECREATE
740 else: 658 wid = self.widgets.getOrCreateWidget(class_, target, *args, **kwargs)
741 tab = self.addTab(new_tab, lib_wid, False) 659 self.setSelected(wid)
742 else: # reuse existing widget 660 return wid
743 tab = lib_wid.getWidgetsPanel(expect=False) 661
744 if new_tab is None: 662 if new_tab:
745 if tab is not None: 663 kwargs['on_new_widget'] = None
746 tab.removeWidget(lib_wid) 664 kwargs['on_existing_widget'] = C.WIDGET_RECREATE
747 self.addWidget(lib_wid) 665 wid = self.widgets.getOrCreateWidget(class_, target, *args, **kwargs)
748 if select: 666 self.tab_panel.addWidgetsTab(new_tab)
749 if new_tab is not None: 667 self.addWidget(wid, tab_index=self.tab_panel.getWidgetCount() - 1)
750 self.tab_panel.selectTab(tab) 668 return wid
751 # must be done after the widget is added, 669
752 # for example to scroll to the bottom 670 kwargs['on_existing_widget'] = C.WIDGET_RAISE
753 self.setSelected(lib_wid)
754 lib_wid.refresh()
755 return lib_wid
756
757 def getRoomWidget(self, target):
758 """Get the MUC widget for the given target.
759
760 @param target (jid.JID): BARE jid of the MUC
761 @return: panels.ChatPanel instance or None
762 """
763 entity = {'item': target, 'type_': 'group'}
764 if target.full() in self.room_list or target in self.room_list: # as JID is a string-based class, we don't know what will please Pyjamas...
765 return self.getLiberviaWidget(panels.ChatPanel, entity, ignoreOtherTabs=False)
766 return None
767
768 def getOrCreateRoomWidget(self, target):
769 """Get the MUC widget for the given target, create it if necessary.
770
771 @param target (jid.JID): BARE jid of the MUC
772 @return: panels.ChatPanel instance
773 """
774 lib_wid = self.getRoomWidget(target)
775 if lib_wid:
776 return lib_wid
777
778 # XXX: target.node.startwith(...) raises an error "startswith is not a function"
779 # This happens when node a is property defined in the JID class
780 # FIXME: pyjamas doesn't handle the properties well
781 node = target.node
782
783 # XXX: it's not really beautiful, but it works :)
784 if node.startswith('sat_tarot_'):
785 tab_name = "Tarot"
786 elif node.startswith('sat_radiocol_'):
787 tab_name = "Radio collective"
788 else:
789 tab_name = target.node
790
791 self.room_list.append(target)
792 entity = {'item': target, 'type_': 'group'}
793 return self.getOrCreateLiberviaWidget(panels.ChatPanel, entity, new_tab=tab_name)
794
795 def _newMessageCb(self, from_jid_s, msg, msg_type, to_jid_s, extra):
796 from_jid = jid.JID(from_jid_s)
797 to_jid = jid.JID(to_jid_s)
798 for plugin in self.plugins.values():
799 if hasattr(plugin, 'messageReceivedTrigger'):
800 if not plugin.messageReceivedTrigger(from_jid, msg, msg_type, to_jid, extra):
801 return # plugin returned False to interrupt the process
802 self.newMessageCb(from_jid, msg, msg_type, to_jid, extra)
803
804 def newMessageCb(self, from_jid, msg, msg_type, to_jid, extra):
805 other = to_jid if from_jid.bare == self.whoami.bare else from_jid
806 lib_wid = self.getLiberviaWidget(panels.ChatPanel, {'item': other}, ignoreOtherTabs=False)
807 self.displayNotification(from_jid, msg)
808 if msg_type == 'headline' and from_jid.full() == self._defaultDomain:
809 try:
810 assert extra['subject'] # subject is defined and not empty
811 title = extra['subject']
812 except (KeyError, AssertionError):
813 title = _('Announcement from %s') % from_jid.full()
814 msg = strings.addURLToText(html_tools.XHTML2Text(msg))
815 dialog.InfoDialog(title, msg).show()
816 return
817 if lib_wid is not None:
818 if msg_type == C.MESS_TYPE_INFO:
819 lib_wid.printInfo(msg)
820 else:
821 lib_wid.printMessage(from_jid, msg, extra)
822 if 'header_info' in extra:
823 lib_wid.setHeaderInfo(extra['header_info'])
824 else:
825 # FIXME: "info" message and header info will be lost here
826 if not self.contact_panel.isContactInRoster(other.bare):
827 self.contact_panel.updateContact(other.bare, {}, [C.GROUP_NOT_IN_ROSTER])
828 # The message has not been shown, we must indicate it
829 self.contact_panel.setContactMessageWaiting(other.bare, True)
830
831 def _presenceUpdateCb(self, entity, show, priority, statuses):
832 entity_jid = jid.JID(entity)
833 if self.whoami and self.whoami == entity_jid: # XXX: QnD way to get our presence/status
834 assert(isinstance(self.status_panel, panels.PresenceStatusPanel))
835 self.status_panel.setPresence(show) # pylint: disable=E1103
836 if statuses:
837 self.status_panel.setStatus(statuses.values()[0]) # pylint: disable=E1103
838 else:
839 bare_jid = entity_jid.bareJID()
840 if bare_jid.full() in self.room_list or bare_jid in self.room_list: # as JID is a string-based class, we don't know what will please Pyjamas...
841 wid = self.getRoomWidget(bare_jid)
842 else:
843 wid = self.contact_panel
844 if show == 'unavailable': # XXX: save some resources as for now we only need 'unavailable'
845 for plugin in self.plugins.values():
846 if hasattr(plugin, 'presenceReceivedTrigger'):
847 plugin.presenceReceivedTrigger(entity_jid, show, priority, statuses)
848 if wid:
849 wid.setConnected(entity_jid.bare, entity_jid.resource, show, priority, statuses)
850
851 def _roomJoinedCb(self, room_jid_s, room_nicks, user_nick):
852 chat_panel = self.getOrCreateRoomWidget(jid.JID(room_jid_s))
853 chat_panel.setUserNick(user_nick)
854 chat_panel.setPresents(room_nicks)
855 chat_panel.refresh()
856
857 def _roomLeftCb(self, room_jid_s, room_nicks, user_nick):
858 try: 671 try:
859 del self.room_list[room_jid_s] 672 wid = self.widgets.getOrCreateWidget(class_, target, *args, **kwargs)
860 except KeyError: 673 except quick_widgets.WidgetAlreadyExistsError:
861 try: # as JID is a string-based class, we don't know what will please Pyjamas... 674 kwargs['on_existing_widget'] = C.WIDGET_KEEP
862 del self.room_list[jid.JID(room_jid_s)] 675 wid = self.widgets.getOrCreateWidget(class_, target, *args, **kwargs)
863 except KeyError: 676 widgets_panel = wid.getParent(libervia_widget.WidgetsPanel, expect=False)
864 pass 677 if widgets_panel is None:
865 678 # The widget exists but is hidden
866 def _roomUserJoinedCb(self, room_jid_s, user_nick, user_data): 679 self.addWidget(wid)
867 lib_wid = self.getOrCreateRoomWidget(jid.JID(room_jid_s)) 680 elif widgets_panel != self.tab_panel.getCurrentPanel():
868 if lib_wid: 681 # the widget is on an other tab, so we add a new one here
869 lib_wid.userJoined(user_nick, user_data) 682 kwargs['on_existing_widget'] = C.WIDGET_RECREATE
870 683 wid = self.widgets.getOrCreateWidget(class_, target, *args, **kwargs)
871 def _roomUserLeftCb(self, room_jid_s, user_nick, user_data): 684 self.addWidget(wid)
872 lib_wid = self.getRoomWidget(jid.JID(room_jid_s)) 685 self.setSelected(wid)
873 if lib_wid: 686 return wid
874 lib_wid.userLeft(user_nick, user_data) 687
875 688
876 def _roomUserChangedNickCb(self, room_jid_s, old_nick, new_nick): 689 # def getOrCreateLiberviaWidget(self, class_, entity, select=True, new_tab=None):
877 """Called when an user joined a MUC room""" 690 # """Get the matching LiberviaWidget if it exists, or create a new one.
878 lib_wid = self.getRoomWidget(jid.JID(room_jid_s)) 691 # @param class_ (class): class of the panel (ChatPanel, MicroblogPanel...)
879 if lib_wid: 692 # @param entity (dict): dictionnary to define the entity.
880 lib_wid.changeUserNick(old_nick, new_nick) 693 # @param select (bool): if True, select the widget that has been found or created
881 694 # @param new_tab (unicode): if not None, a widget which is created is created in
882 def _tarotGameStartedCb(self, waiting, room_jid_s, referee, players): 695 # a new tab. In that case new_tab is a unicode to label that new tab.
883 lib_wid = self.getRoomWidget(jid.JID(room_jid_s)) 696 # If new_tab is not None and a widget is found, no tab is created.
884 if lib_wid: 697 # @return: the newly created wigdet if REUSE_EXISTING_LIBERVIA_WIDGETS
885 lib_wid.startGame("Tarot", waiting, referee, players) 698 # is set to False or if the widget has not been found, the existing
886 699 # widget that has been found otherwise."""
887 def _tarotGameGenericCb(self, event_name, room_jid_s, args): 700 # lib_wid = None
888 lib_wid = self.getRoomWidget(jid.JID(room_jid_s)) 701 # tab = None
889 if lib_wid: 702 # if REUSE_EXISTING_LIBERVIA_WIDGETS:
890 getattr(lib_wid.getGame("Tarot"), event_name)(*args) 703 # lib_wid = self.getLiberviaWidget(class_, entity, new_tab is None)
891 704 # if lib_wid is None: # create a new widget
892 def _radioColStartedCb(self, waiting, room_jid_s, referee, players, queue_data): 705 # lib_wid = class_.createPanel(self, *(entity.values())) # XXX: passing **entity bugs!
893 lib_wid = self.getRoomWidget(jid.JID(room_jid_s)) 706 # if new_tab is None:
894 if lib_wid: 707 # self.addWidget(lib_wid)
895 lib_wid.startGame("RadioCol", waiting, referee, players, queue_data) 708 # else:
896 709 # tab = self.addTab(new_tab, lib_wid, False)
897 def _radioColGenericCb(self, event_name, room_jid_s, args): 710 # else: # reuse existing widget
898 lib_wid = self.getRoomWidget(jid.JID(room_jid_s)) 711 # tab = lib_wid.getWidgetsPanel(expect=False)
899 if lib_wid: 712 # if new_tab is None:
900 getattr(lib_wid.getGame("RadioCol"), event_name)(*args) 713 # if tab is not None:
714 # tab.removeWidget(lib_wid)
715 # self.addWidget(lib_wid)
716 # if select:
717 # if new_tab is not None:
718 # self.tab_panel.selectTab(tab)
719 # # must be done after the widget is added,
720 # # for example to scroll to the bottom
721 # self.setSelected(lib_wid)
722 # lib_wid.refresh()
723 # return lib_wid
724
725 # def getRoomWidget(self, target):
726 # """Get the MUC widget for the given target.
727
728 # @param target (jid.JID): BARE jid of the MUC
729 # @return: panel.ChatPanel instance or None
730 # """
731 # entity = {'item': target, 'type_': 'group'}
732 # if target.full() in self.room_list or target in self.room_list: # as JID is a string-based class, we don't know what will please Pyjamas...
733 # return self.getLiberviaWidget(panel.ChatPanel, entity, ignoreOtherTabs=False)
734 # return None
735
736 # def getOrCreateRoomWidget(self, target):
737 # """Get the MUC widget for the given target, create it if necessary.
738
739 # @param target (jid.JID): BARE jid of the MUC
740 # @return: panel.ChatPanel instance
741 # """
742 # lib_wid = self.getRoomWidget(target)
743 # if lib_wid:
744 # return lib_wid
745
746 # # XXX: target.node.startwith(...) raises an error "startswith is not a function"
747 # # This happens when node a is property defined in the JID class
748 # # FIXME: pyjamas doesn't handle the properties well
749 # node = target.node
750
751 # # XXX: it's not really beautiful, but it works :)
752 # if node.startswith('sat_tarot_'):
753 # tab_name = "Tarot"
754 # elif node.startswith('sat_radiocol_'):
755 # tab_name = "Radio collective"
756 # else:
757 # tab_name = target.node
758
759 # self.room_list.append(target)
760 # entity = {'item': target, 'type_': 'group'}
761 # return self.getOrCreateLiberviaWidget(panel.ChatPanel, entity, new_tab=tab_name)
762
763 # def _newMessageCb(self, from_jid_s, msg, msg_type, to_jid_s, extra):
764 # from_jid = jid.JID(from_jid_s)
765 # to_jid = jid.JID(to_jid_s)
766 # for plugin in self.plugins.values():
767 # if hasattr(plugin, 'messageReceivedTrigger'):
768 # if not plugin.messageReceivedTrigger(from_jid, msg, msg_type, to_jid, extra):
769 # return # plugin returned False to interrupt the process
770 # self.newMessageCb(from_jid, msg, msg_type, to_jid, extra)
771
772 # def newMessageCb(self, from_jid, msg, msg_type, to_jid, extra):
773 # other = to_jid if from_jid.bare == self.whoami.bare else from_jid
774 # lib_wid = self.getLiberviaWidget(panel.ChatPanel, {'item': other}, ignoreOtherTabs=False)
775 # self.displayNotification(from_jid, msg)
776 # if msg_type == 'headline' and from_jid.full() == self._defaultDomain:
777 # try:
778 # assert extra['subject'] # subject is defined and not empty
779 # title = extra['subject']
780 # except (KeyError, AssertionError):
781 # title = _('Announcement from %s') % from_jid.full()
782 # msg = strings.addURLToText(html_tools.XHTML2Text(msg))
783 # dialog.InfoDialog(title, msg).show()
784 # return
785 # if lib_wid is not None:
786 # if msg_type == C.MESS_TYPE_INFO:
787 # lib_wid.printInfo(msg)
788 # else:
789 # lib_wid.printMessage(from_jid, msg, extra)
790 # if 'header_info' in extra:
791 # lib_wid.setHeaderInfo(extra['header_info'])
792 # else:
793 # # FIXME: "info" message and header info will be lost here
794 # if not self.contact_panel.isContactInRoster(other.bare):
795 # self.contact_panel.updateContact(other.bare, {}, [C.GROUP_NOT_IN_ROSTER])
796 # # The message has not been shown, we must indicate it
797 # self.contact_panel.setContactMessageWaiting(other.bare, True)
798
799 # def _presenceUpdateCb(self, entity, show, priority, statuses):
800 # entity_jid = jid.JID(entity)
801 # if self.whoami and self.whoami == entity_jid: # XXX: QnD way to get our presence/status
802 # assert(isinstance(self.status_panel, main_panel.PresenceStatusPanel))
803 # self.status_panel.setPresence(show) # pylint: disable=E1103
804 # if statuses:
805 # self.status_panel.setStatus(statuses.values()[0]) # pylint: disable=E1103
806 # else:
807 # bare_jid = entity_jid.bareJID()
808 # if bare_jid.full() in self.room_list or bare_jid in self.room_list: # as JID is a string-based class, we don't know what will please Pyjamas...
809 # wid = self.getRoomWidget(bare_jid)
810 # else:
811 # wid = self.contact_panel
812 # if show == 'unavailable': # XXX: save some resources as for now we only need 'unavailable'
813 # for plugin in self.plugins.values():
814 # if hasattr(plugin, 'presenceReceivedTrigger'):
815 # plugin.presenceReceivedTrigger(entity_jid, show, priority, statuses)
816 # if wid:
817 # wid.setConnected(entity_jid.bare, entity_jid.resource, show, priority, statuses)
818
819 # def _roomJoinedCb(self, room_jid_s, room_nicks, user_nick):
820 # chat_panel = self.getOrCreateRoomWidget(jid.JID(room_jid_s))
821 # chat_panel.setUserNick(user_nick)
822 # chat_panel.setPresents(room_nicks)
823 # chat_panel.refresh()
824
825 # def _roomLeftCb(self, room_jid_s, room_nicks, user_nick):
826 # try:
827 # del self.room_list[room_jid_s]
828 # except KeyError:
829 # try: # as JID is a string-based class, we don't know what will please Pyjamas...
830 # del self.room_list[jid.JID(room_jid_s)]
831 # except KeyError:
832 # pass
833
834 # def _roomUserJoinedCb(self, room_jid_s, user_nick, user_data):
835 # lib_wid = self.getOrCreateRoomWidget(jid.JID(room_jid_s))
836 # if lib_wid:
837 # lib_wid.userJoined(user_nick, user_data)
838
839 # def _roomUserLeftCb(self, room_jid_s, user_nick, user_data):
840 # lib_wid = self.getRoomWidget(jid.JID(room_jid_s))
841 # if lib_wid:
842 # lib_wid.userLeft(user_nick, user_data)
843
844 # def _roomUserChangedNickCb(self, room_jid_s, old_nick, new_nick):
845 # """Called when an user joined a MUC room"""
846 # lib_wid = self.getRoomWidget(jid.JID(room_jid_s))
847 # if lib_wid:
848 # lib_wid.changeUserNick(old_nick, new_nick)
849
850 # def _tarotGameStartedCb(self, waiting, room_jid_s, referee, players):
851 # lib_wid = self.getRoomWidget(jid.JID(room_jid_s))
852 # if lib_wid:
853 # lib_wid.startGame("Tarot", waiting, referee, players)
854
855 # def _tarotGameGenericCb(self, event_name, room_jid_s, args):
856 # lib_wid = self.getRoomWidget(jid.JID(room_jid_s))
857 # if lib_wid:
858 # getattr(lib_wid.getGame("Tarot"), event_name)(*args)
859
860 # def _radioColStartedCb(self, waiting, room_jid_s, referee, players, queue_data):
861 # lib_wid = self.getRoomWidget(jid.JID(room_jid_s))
862 # if lib_wid:
863 # lib_wid.startGame("RadioCol", waiting, referee, players, queue_data)
864
865 # def _radioColGenericCb(self, event_name, room_jid_s, args):
866 # lib_wid = self.getRoomWidget(jid.JID(room_jid_s))
867 # if lib_wid:
868 # getattr(lib_wid.getGame("RadioCol"), event_name)(*args)
901 869
902 def _getPresenceStatusesCb(self, presence_data): 870 def _getPresenceStatusesCb(self, presence_data):
903 for entity in presence_data: 871 for entity in presence_data:
904 for resource in presence_data[entity]: 872 for resource in presence_data[entity]:
905 args = presence_data[entity][resource] 873 args = presence_data[entity][resource]
927 #The user want to subscribe to our presence 895 #The user want to subscribe to our presence
928 _dialog = None 896 _dialog = None
929 msg = HTML('The contact <b>%s</b> want to add you in his/her contact list, do you accept ?' % html_tools.html_sanitize(entity)) 897 msg = HTML('The contact <b>%s</b> want to add you in his/her contact list, do you accept ?' % html_tools.html_sanitize(entity))
930 898
931 def ok_cb(ignore): 899 def ok_cb(ignore):
932 self.bridge.call('subscription', None, "subscribed", entity, '', _dialog.getSelectedGroups()) 900 self.bridge.call('subscription', None, "subscribed", entity)
901 self.bridge.updateContact(entity, '', _dialog.getSelectedGroups())
933 902
934 def cancel_cb(ignore): 903 def cancel_cb(ignore):
935 self.bridge.call('subscription', None, "unsubscribed", entity, '', '') 904 self.bridge.call('subscription', None, "unsubscribed", entity, '', '')
936 905
937 _dialog = dialog.GroupSelector([msg], self.contact_panel.getGroups(), [], "Add", ok_cb, cancel_cb) 906 _dialog = dialog.GroupSelector([msg], self.contact_panel.getGroups(), [], "Add", ok_cb, cancel_cb)
943 912
944 def _newContactCb(self, contact_jid, attributes, groups): 913 def _newContactCb(self, contact_jid, attributes, groups):
945 self.contact_panel.updateContact(contact_jid, attributes, groups) 914 self.contact_panel.updateContact(contact_jid, attributes, groups)
946 915
947 def _entityDataUpdatedCb(self, entity_jid_s, key, value): 916 def _entityDataUpdatedCb(self, entity_jid_s, key, value):
917 raise Exception # FIXME should not be here
948 if key == "avatar": 918 if key == "avatar":
949 avatar = '/' + C.AVATARS_DIR + value 919 avatar = '/' + C.AVATARS_DIR + value
950 self.avatars_cache[entity_jid_s] = avatar 920 self.avatars_cache[entity_jid_s] = avatar
951 self.contact_panel.updateAvatar(entity_jid_s, avatar) 921 self.contact_panel.updateAvatar(entity_jid_s, avatar)
952 922
953 for lib_wid in self.libervia_widgets: 923 for lib_wid in self.libervia_widgets:
954 if isinstance(lib_wid, panels.MicroblogPanel): 924 if isinstance(lib_wid, blog.MicroblogPanel):
955 if lib_wid.isJidAccepted(entity_jid_s) or (self.whoami and entity_jid_s == self.whoami.bare): 925 if lib_wid.isJidAccepted(entity_jid_s) or (self.whoami and entity_jid_s == self.whoami.bare):
956 lib_wid.updateValue('avatar', entity_jid_s, avatar) 926 lib_wid.updateValue('avatar', entity_jid_s, avatar)
957 927
958 def _chatStateReceivedCb(self, from_jid_s, state): 928 # def _chatStateReceivedCb(self, from_jid_s, state):
959 """Callback when a new chat state is received. 929 # """Callback when a new chat state is received.
960 @param from_jid_s: JID of the contact who sent his state, or '@ALL@' 930 # @param from_jid_s: JID of the contact who sent his state, or '@ALL@'
961 @param state: new state (string) 931 # @param state (unicode): new state
962 """ 932 # """
963 if from_jid_s == '@ALL@': 933 # if from_jid_s == '@ALL@':
964 for lib_wid in self.libervia_widgets: 934 # for lib_wid in self.libervia_widgets:
965 if isinstance(lib_wid, panels.ChatPanel): 935 # if isinstance(lib_wid, panel.ChatPanel):
966 lib_wid.setState(state, nick=C.ALL_OCCUPANTS) 936 # lib_wid.setState(state, nick=C.ALL_OCCUPANTS)
967 return 937 # return
968 from_jid = jid.JID(from_jid_s) 938 # from_jid = jid.JID(from_jid_s)
969 lib_wid = self.getLiberviaWidget(panels.ChatPanel, {'item': from_jid}, ignoreOtherTabs=False) 939 # lib_wid = self.getLiberviaWidget(panel.ChatPanel, {'item': from_jid}, ignoreOtherTabs=False)
970 lib_wid.setState(state, nick=from_jid.resource) 940 # lib_wid.setState(state, nick=from_jid.resource)
971 941
972 def _askConfirmation(self, confirmation_id, confirmation_type, data): 942 def askConfirmationHandler(self, confirmation_id, confirmation_type, data):
973 answer_data = {} 943 answer_data = {}
974 944
975 def confirm_cb(result): 945 def confirm_cb(result):
976 self.bridge.call('confirmationAnswer', None, confirmation_id, result, answer_data) 946 self.bridge.call('confirmationAnswer', None, confirmation_id, result, answer_data)
977 947
994 break 964 break
995 965
996 def getCachedParam(self, category, name): 966 def getCachedParam(self, category, name):
997 """Return a parameter cached value (e.g for refreshing the UI) 967 """Return a parameter cached value (e.g for refreshing the UI)
998 968
999 @param category (str): the parameter category 969 @param category (unicode): the parameter category
1000 @pram name (str): the parameter name 970 @pram name (unicode): the parameter name
1001 """ 971 """
1002 return self.cached_params[(category, name)] if (category, name) in self.cached_params else None 972 return self.cached_params[(category, name)] if (category, name) in self.cached_params else None
1003 973
1004 def sendError(self, errorData): 974 def sendError(self, errorData):
1005 dialog.InfoDialog("Error while sending message", 975 dialog.InfoDialog("Error while sending message",
1006 "Your message can't be sent", Width="400px").center() 976 "Your message can't be sent", Width="400px").center()
1007 log.error("sendError: %s" % str(errorData)) 977 log.error("sendError: %s" % unicode(errorData))
1008 978
1009 def send(self, targets, text, extra={}): 979 # FIXME: this method is fat too complicated and depend of widget type
1010 """Send a message to any target type. 980 # must be refactored and moved to each widget instead
1011 @param targets: list of tuples (type, entities, addr) with: 981 # def send(self, targets, text, extra={}):
1012 - type in ("PUBLIC", "GROUP", "COMMENT", "STATUS" , "groupchat" , "chat") 982 # """Send a message to any target type.
1013 - entities could be a JID, a list groups, a node hash... depending the target 983 # @param targets: list of tuples (type, entities, addr) with:
1014 - addr in ("To", "Cc", "Bcc") - ignore case 984 # - type in ("PUBLIC", "GROUP", "COMMENT", "STATUS" , "groupchat" , "chat")
1015 @param text: the message content 985 # - entities could be a JID, a list groups, a node hash... depending the target
1016 @param extra: options 986 # - addr in ("To", "Cc", "Bcc") - ignore case
1017 """ 987 # @param text: the message content
1018 # FIXME: too many magic strings, we should use constants instead 988 # @param extra: options
1019 addresses = [] 989 # """
1020 for target in targets: 990 # # FIXME: too many magic strings, we should use constants instead
1021 type_, entities, addr = target[0], target[1], 'to' if len(target) < 3 else target[2].lower() 991 # addresses = []
1022 if type_ in ("PUBLIC", "GROUP"): 992 # for target in targets:
1023 self.bridge.call("sendMblog", None, type_, entities if type_ == "GROUP" else None, text, extra) 993 # type_, entities, addr = target[0], target[1], 'to' if len(target) < 3 else target[2].lower()
1024 elif type_ == "COMMENT": 994 # if type_ in ("PUBLIC", "GROUP"):
1025 self.bridge.call("sendMblogComment", None, entities, text, extra) 995 # self.bridge.call("sendMblog", None, type_, entities if type_ == "GROUP" else None, text, extra)
1026 elif type_ == "STATUS": 996 # elif type_ == "COMMENT":
1027 assert(isinstance(self.status_panel, panels.PresenceStatusPanel)) 997 # self.bridge.call("sendMblogComment", None, entities, text, extra)
1028 self.bridge.call('setStatus', None, self.status_panel.presence, text) # pylint: disable=E1103 998 # elif type_ == "STATUS":
1029 elif type_ in ("groupchat", "chat"): 999 # assert(isinstance(self.status_panel, main_panel.PresenceStatusPanel))
1030 addresses.append((addr, entities)) 1000 # self.bridge.call('setStatus', None, self.status_panel.presence, text) # pylint: disable=E1103
1031 else: 1001 # elif type_ in ("groupchat", "chat"):
1032 log.error("Unknown target type") 1002 # addresses.append((addr, entities))
1033 if addresses: 1003 # else:
1034 if len(addresses) == 1 and addresses[0][0] == 'to': 1004 # log.error("Unknown target type")
1035 to_jid_s = addresses[0][1] 1005 # if addresses:
1036 for plugin in self.plugins.values(): 1006 # if len(addresses) == 1 and addresses[0][0] == 'to':
1037 if hasattr(plugin, 'sendMessageTrigger'): 1007 # to_jid_s = addresses[0][1]
1038 if not plugin.sendMessageTrigger(jid.JID(to_jid_s), text, type_, extra): 1008 # for plugin in self.plugins.values():
1039 return # plugin returned False to interrupt the process 1009 # if hasattr(plugin, 'sendMessageTrigger'):
1040 self.bridge.call('sendMessage', (None, self.sendError), to_jid_s, text, '', type_, extra) 1010 # if not plugin.sendMessageTrigger(jid.JID(to_jid_s), text, type_, extra):
1041 else: 1011 # return # plugin returned False to interrupt the process
1042 extra.update({'address': '\n'.join([('%s:%s' % entry) for entry in addresses])}) 1012 # self.bridge.call('sendMessage', (None, self.sendError), to_jid_s, text, '', type_, extra)
1043 self.bridge.call('sendMessage', (None, self.sendError), self.whoami.domain, text, '', type_, extra) 1013 # else:
1014 # extra.update({'address': '\n'.join([('%s:%s' % entry) for entry in addresses])})
1015 # self.bridge.call('sendMessage', (None, self.sendError), self.whoami.domain, text, '', type_, extra)
1044 1016
1045 def showWarning(self, type_=None, msg=None): 1017 def showWarning(self, type_=None, msg=None):
1046 """Display a popup information message, e.g. to notify the recipient of a message being composed. 1018 """Display a popup information message, e.g. to notify the recipient of a message being composed.
1047 If type_ is None, a popup being currently displayed will be hidden. 1019 If type_ is None, a popup being currently displayed will be hidden.
1048 @type_: a type determining the CSS style to be applied (see WarningPopup.showWarning) 1020 @type_: a type determining the CSS style to be applied (see WarningPopup.showWarning)
1049 @msg: message to be displayed 1021 @msg: message to be displayed
1050 """ 1022 """
1051 if not hasattr(self, "warning_popup"): 1023 if not hasattr(self, "warning_popup"):
1052 self.warning_popup = panels.WarningPopup() 1024 self.warning_popup = main_panel.WarningPopup()
1053 self.warning_popup.showWarning(type_, msg) 1025 self.warning_popup.showWarning(type_, msg)
1026
1027 def showDialog(self, message, title="", type_="info", answer_cb=None, answer_data=None):
1028 if type_ == 'info':
1029 popup = dialog.InfoDialog(unicode(title), unicode(message), callback=answer_cb)
1030 elif type_ == 'error':
1031 popup = dialog.InfoDialog(unicode(title), unicode(message), callback=answer_cb)
1032 elif type_ == 'yes/no':
1033 popup = dialog.ConfirmDialog(lambda answer: answer_cb(answer, answer_data),
1034 text=unicode(message), title=unicode(title))
1035 popup.cancel_button.setText(_("No"))
1036 else:
1037 popup = dialog.InfoDialog(unicode(title), unicode(message), callback=answer_cb)
1038 log.error(_('unmanaged dialog type: %s'), type_)
1039 popup.show()
1054 1040
1055 1041
1056 if __name__ == '__main__': 1042 if __name__ == '__main__':
1057 app = SatWebFrontend() 1043 app = SatWebFrontend()
1058 app.onModuleLoad() 1044 app.onModuleLoad()
1059 pyjd.run() 1045 host_listener.callListeners(app)