view libervia.py @ 14:9bf8ed012adc

- Group microblog management, first draft - Bad connexion issue fixed
author Goffi <goffi@goffi.org>
date Thu, 07 Apr 2011 22:27:36 +0200
parents 0110d4e1d816
children 099c05a0dcab
line wrap: on
line source

#!/usr/bin/python
# -*- coding: utf-8 -*-

"""
Libervia: a Salut à Toi frontend
Copyright (C) 2011  Jérôme Poisson (goffi@goffi.org)

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
"""

import pyjd # this is dummy in pyjs
from pyjamas.ui.SimplePanel import SimplePanel
from pyjamas.ui.RootPanel import RootPanel
from pyjamas.ui.VerticalPanel import VerticalPanel
from pyjamas.ui.HorizontalPanel import HorizontalPanel
from pyjamas.ui.HTMLPanel import HTMLPanel
from pyjamas.ui.Grid import Grid
from pyjamas.ui.Label import Label
from pyjamas.ui import HasAlignment
from pyjamas.ui.MenuBar import MenuBar
from pyjamas.ui.MenuItem import MenuItem
from pyjamas.ui.AutoComplete import AutoCompleteTextBox
from pyjamas.ui.DropWidget import DropWidget
from pyjamas import Window
from pyjamas.JSONService import JSONProxy
from pyjamas.ui.KeyboardListener import KEY_ENTER
from register import RegisterPanel, RegisterBox
from pyjamas import DOM
from contact import ContactPanel
from datetime import datetime


class LiberviaJsonProxy(JSONProxy):
    def __init__(self, *args, **kwargs):
        JSONProxy.__init__(self, *args, **kwargs)
        self.handler=self
        self.cb={}

    def call(self, method, cb, *args):
        if cb:
            self.cb[method] = cb
        self.callMethod(method,args)

    def onRemoteResponse(self, response, request_info):
        if self.cb.has_key(request_info.method):
            self.cb[request_info.method](response)
            del self.cb[request_info.method]
        
    def onRemoteError(self, code, errobj, request_info):
        if code != 0:
            Window.alert("Internal server error")
        else:
            if isinstance(errobj['message'],dict):
                Window.alert("Error %s: %s" % (errobj['message']['faultCode'], errobj['message']['faultString']))
            else:
                Window.alert("Error: %s" % errobj['message'])

class RegisterCall(LiberviaJsonProxy):
    def __init__(self):
        LiberviaJsonProxy.__init__(self, "/register_api",
                        ["isRegistered","isConnected","connect"])
        self.handler=self
        self.cb={}

class BridgeCall(LiberviaJsonProxy):
    def __init__(self):
        LiberviaJsonProxy.__init__(self, "/json_api",
                        ["getContacts", "sendMblog", "getMblogNodes"])

class BridgeSignals(LiberviaJsonProxy):
    def __init__(self):
        LiberviaJsonProxy.__init__(self, "/json_signal_api",
                        ["getSignals"])

class MenuCmd:

    def __init__(self, object, handler):
        self._object  = object
        self._handler = handler

    def execute(self):
        handler = getattr(self._object, self._handler)
        handler()

class Menu(SimplePanel):

    def __init__(self):
        SimplePanel.__init__(self)

        menu_general = MenuBar(vertical=True)
        menu_general.addItem("Properties", MenuCmd(self, "onProperties"))

        menu_games = MenuBar(vertical=True)
        menu_games.addItem("Tarot", MenuCmd(self, "onTarotGame"))
        menu_games.addItem("Xiangqi", MenuCmd(self, "onXiangqiGame"))

        menubar = MenuBar(vertical=False)
        menubar.addItem(MenuItem("General", menu_general))
        menubar.addItem(MenuItem("Games", True, menu_games))
        self.add(menubar)

    def onProperties(self):
        Window.alert("Properties selected")

    def onTarotGame(self):
        Window.alert("Tarot selected")

    def onXiangqiGame(self):
        Window.alert("Xiangqi selected")

class MagicBox(AutoCompleteTextBox):

    def __init__(self, host):
        AutoCompleteTextBox.__init__(self)
        self.host = host

    def addKey(self, key):
        self.getCompletionItems().completions.append(key)

    def onKeyPress(self, sender, keycode, modifiers):
        if keycode == KEY_ENTER and not self.visible: 
            self.host.bridge.call('sendMblog', None, self.getText())
            self.setText('')

    def complete(self):
        #self.visible=False #XXX: self.visible is not unset in pyjamas when ENTER is pressed and a completion is done
        #XXX: fixed directly on pyjamas, if the patch is accepted, no need to walk around this
        return AutoCompleteTextBox.complete(self)

class EmptyPanel(DropWidget, SimplePanel):
    """Empty dropable panel"""

    def __init__(self, host, data):
        SimplePanel.__init__(self)
        self.host = host
        self.data = data
        _panel = HTMLPanel("&nbsp;")
        self.add(_panel)
        self.setHeight('100%')
        DropWidget.__init__(self)
    
    def onDragEnter(self, event):
        self.addStyleName('dragover')
        DOM.eventPreventDefault(event)

    def onDragLeave(self, event):
        self.removeStyleName('dragover')

    def onDragOver(self, event):
        DOM.eventPreventDefault(event)

    def onDrop(self, event):
        print "Empty Panel: onDrop"
        dt = event.dataTransfer
        #'text', 'text/plain', and 'Text' are equivalent.
        try:
            item = dt.getData("text/plain")
            item_type = dt.getData("type")
            print "message: %s" % item
            print "type: %s" % item_type
        except:
            print "no message found"
            item='&nbsp;'
            item_type = None
        DOM.eventPreventDefault(event)
        _mblog = MicroblogPanel(self.host, item)
        if item_type=="GROUP":
            _mblog.setAcceptedGroup(item)
        self.host.mpanels.insert(0,_mblog)
        self.host.middle_panel.changePanel(self.data,self.host.mpanels[0])



class MicroblogEntry(SimplePanel):

    def __init__(self, body, author, timestamp):
        SimplePanel.__init__(self)

        _datetime = datetime.fromtimestamp(timestamp)

        panel = HTMLPanel("<div class='mb_entry_header'><span class='mb_entry_author'>%(author)s</span> on <span class='mb_entry_timestamp'>%(timestamp)s</span></div><div class='mb_entry_body'>%(body)s</div>" %
            {"author": author,
            "timestamp": _datetime,
            "body": body}
            )
        panel.setStyleName('microblogEntry')
        self.add(panel)


class MicroblogPanel(VerticalPanel):

    def __init__(self,host, title='&nbsp;', accept_all=False):
        self.host = host
        self.accept_all = accept_all
        title=title.replace('<','&lt;').replace('>','&gt;')
        VerticalPanel.__init__(self)
        self.accepted_groups = []
        _class = ['mb_panel_header']
        if title == '&nbsp;':
            _class.append('empty_header')
        self.add(HTMLPanel("<div class='%s'>%s</div>" % (','.join(_class),title)))
        self.setStyleName('microblogPanel')

    def addEntry(self, text, author=None, timestamp=None):
        """Add an entry to the panel
        @param text: main text of the entry
        @param author: who wrote the entry
        @param date: when the entry was written"""
        _entry = MicroblogEntry(text, author, timestamp)
        self.add(_entry)

    def setAcceptedGroup(self, group):
        """Set the group which can be displayed in this panel
        @param group: string of the group, or list of string
        """
        if isinstance(group, list):
            self.accepted_groups.extend(group)
        else:
            self.accepted_groups.append(group)

    def isJidAccepted(self, jid):
        """Tell if a jid is actepted and show in this panel
        @param jid: jid
        @return: True if the jid is accepted"""
        if self.accept_all:
            return True
        for group in self.accepted_groups:
            if self.host.contactPanel.isContactInGroup(group, jid):
                return True
        return False


class MiddlePannel(HorizontalPanel):
    
    def __init__(self, host):
        self.host=host
        HorizontalPanel.__init__(self)
        self._left = self.host.contactPanel
        self._right = Grid(1,3)
        self._right.setWidth('100%')
        self._right.setHeight('100%')
        self.add(self._left)
        self.setCellWidth(self._left, "15%")
        self.add(self._right)
        self.setCellWidth(self._right, "85%")
        self.setHeight('100%')

    def changePanel(self, idx, panel):
        print "panel:",panel
        print "idx:",idx
        self._right.setWidget(0,idx,panel)

class MainPanel(VerticalPanel):

    def __init__(self, host):
        self.host=host
        VerticalPanel.__init__(self)

        self.setHorizontalAlignment(HasAlignment.ALIGN_LEFT)
        self.setVerticalAlignment(HasAlignment.ALIGN_TOP)

        menu = Menu()
        magic_box = host.magicBox
        self.middle_panel = MiddlePannel(self.host)
        self.middle_panel.setWidth('100%')

        self.add(menu)
        self.add(magic_box)
        self.add(self.middle_panel)
        
        self.setCellHeight(menu, "5%")
        self.setCellHeight(magic_box, "5%")
        self.setCellVerticalAlignment(magic_box, HasAlignment.ALIGN_CENTER)
        self.setCellHorizontalAlignment(magic_box, HasAlignment.ALIGN_CENTER)
        self.setCellHeight(self.middle_panel, "90%")
        self.setCellWidth(self.middle_panel, "100%")

        self.setWidth("100%")
        self.setHeight("100%")

class SatWebFrontend:
    def onModuleLoad(self):
        self.bridge = BridgeCall()
        self.bridge_signals = BridgeSignals()
        self.magicBox = MagicBox(self)
        self.magicBox.addKey("@@: ")
        self.contactPanel = ContactPanel(self)
        self.panel = MainPanel(self)
        self.middle_panel = self.panel.middle_panel
        self.mpanels = [MicroblogPanel(self, accept_all=True)]
        self.middle_panel.changePanel(1,self.mpanels[0])
        self.middle_panel.changePanel(0,EmptyPanel(self, 0))
        self.middle_panel.changePanel(2,EmptyPanel(self, 2))
        self._dialog = None
        RootPanel().add(self.panel)
        self._register = RegisterCall()
        self._register.call('isRegistered',self._isRegisteredCB)

    def _isRegisteredCB(self, registered):
        if not registered:
            self._dialog = RegisterBox(self.logged,centered=True)
            self._dialog.show()
        else:
            self._register.call('isConnected', self._isConnectedCB)

    def _isConnectedCB(self, connected):
        if not connected:
            self._register.call('connect',self.logged)
        else:
            self.logged()

    def logged(self):
        if self._dialog:
            self._dialog.hide()
            del self._dialog # don't work if self._dialog is None
        
        #it's time to fill the page
        self.bridge.call('getContacts', self._getContactsCB)
        self.bridge_signals.call('getSignals', self._getSignalsCB)

    def _getContactsCB(self, contacts_data):
        for contact in contacts_data:
            jid, attributes, groups = contact
            self.contactPanel.addContact(jid, attributes, groups)

    def _getSignalsCB(self, signal_data):
        bridge_signals = BridgeSignals()
        bridge_signals.call('getSignals', self._getSignalsCB)
        print('Got signal ==> name: %s, params: %s' % (signal_data[0],signal_data[1]))
        name,args = signal_data
        if name == 'personalEvent':
            self._personalEventCb(*args)

    ## Signals callbacks ##

    def _personalEventCb(self, sender, event_type, data, profile):
        if event_type == "MICROBLOG":
            if not data.has_key('content'):
                print ("WARNING: No content found in microblog data")
                return
            for panel in self.mpanels:
                if isinstance(panel,MicroblogPanel) and panel.isJidAccepted(sender):
                    print "sender:",sender
                    content = data['content']
                    author = data.get('author')
                    print "timestamp: %s" % data.get('timestamp')
                    timestamp = float(data.get('timestamp',0)) #XXX: int doesn't work here
                    panel.addEntry(content, author, timestamp)

if __name__ == '__main__':
    pyjd.setup("http://localhost:8080/libervia.html")
    app = SatWebFrontend()
    app.onModuleLoad()
    pyjd.run()