view browser_side/menu.py @ 332:6abd099c7007

browser side: sat_frontends.tools.xml is now called xmltools
author Goffi <goffi@goffi.org>
date Tue, 04 Feb 2014 16:49:20 +0100
parents 4f221f34bdc7
children 629c99bbd031
line wrap: on
line source

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

"""
Libervia: a Salut à Toi frontend
Copyright (C) 2011, 2012, 2013 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.VerticalPanel import VerticalPanel
from pyjamas.ui.HorizontalPanel import HorizontalPanel
from pyjamas.ui.DialogBox import DialogBox
from pyjamas.ui.FormPanel import FormPanel
from pyjamas.ui.FileUpload import FileUpload
from pyjamas.ui.MenuBar import MenuBar
from pyjamas.ui.MenuItem import MenuItem
from pyjamas.ui.ListBox import ListBox
from pyjamas.ui.Label import Label
from pyjamas.ui.TextBox import TextBox
from pyjamas.ui.Button import Button
from pyjamas.ui.HTML import HTML
from pyjamas.ui.Frame import Frame
from pyjamas import Window
from jid import JID
from tools import html_sanitize
from tools import FilterFileUpload
from xmlui import XMLUI
import panels
import dialog
from contact_group import ContactGroupEditor
import re


class MenuCmd:

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

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


class LiberviaMenuBar(MenuBar):

    def __init__(self):
        MenuBar.__init__(self, vertical=False)
        self.setStyleName('gwt-MenuBar-horizontal') # XXX: workaround for the Pyjamas' class name fix (it's now "gwt-MenuBar gwt-MenuBar-horizontal")
                                                    # TODO: properly adapt CSS to the new class name

    def doItemAction(self, item, fireCommand):
        MenuBar.doItemAction(self, item, fireCommand)
        if item == self.items[-1] and self.popup:
            self.popup.setPopupPosition(Window.getClientWidth() -
                                        self.popup.getOffsetWidth() - 22,
                                        self.getAbsoluteTop() +
                                        self.getOffsetHeight() - 1)
            self.popup.addStyleName('menuLastPopup')


class AvatarUpload(FormPanel):

    def __init__(self, close_cb=None):
        FormPanel.__init__(self)
        self.close_cb = close_cb
        self.setEncoding(FormPanel.ENCODING_MULTIPART)
        self.setMethod(FormPanel.METHOD_POST)
        self.setAction("upload_avatar")
        self.vPanel = VerticalPanel()
        self.message = HTML('Please select an image to show as your avatar...<br>Your picture must be a square and will be resized to 64x64 pixels if necessary')
        self.vPanel.add(self.message)

        hPanel = HorizontalPanel()
        hPanel.setSpacing(5)
        self.file_upload = FilterFileUpload("avatar_path", 2)
        self.vPanel.add(self.file_upload)

        hPanel.add(Button("Cancel", getattr(self, "onCloseBtnClick")))
        self.upload_btn = Button("Upload avatar", getattr(self, "onSubmitBtnClick"))
        hPanel.add(self.upload_btn)

        self.status = Label()
        hPanel.add(self.status)

        self.vPanel.add(hPanel)

        self.add(self.vPanel)
        self.addFormHandler(self)

    def setCloseCb(self, close_cb):
        self.close_cb = close_cb

    def onCloseBtnClick(self):
        if self.close_cb:
            self.close_cb()
        else:
            print ("WARNING: no close method defined")

    def onSubmitBtnClick(self):
        if not self.file_upload.check():
            return
        self.message.setHTML('<strong>Submitting, please wait...</strong>')
        self.upload_btn.setEnabled(False)
        self.submit()

    def onSubmit(self, event):
        pass

    def onSubmitComplete(self, event):
        result = event.getResults()
        if result != "OK":
            Window.alert("Can't open image... did you actually submit an image?")
            self.message.setHTML('Please select another image file')
            self.upload_btn.setEnabled(True)
        else:
            Window.alert("Your new profile picture has been set!")
            self.close_cb()


class Menu(SimplePanel):

    def __init__(self, host):
        self.host = host
        SimplePanel.__init__(self)
        self.setStyleName('menuContainer')
        _item_tpl = "<img src='media/icons/menu/%s_menu_red.png' />%s"

        menu_general = MenuBar(vertical=True)
        menu_general.addItem("Web widget", MenuCmd(self, "onWebWidget"))
        menu_general.addItem("Disconnect", MenuCmd(self, "onDisconnect"))

        menu_contacts = MenuBar(vertical=True)
        menu_contacts.addItem("Add contact", MenuCmd(self, "onAddContact"))
        menu_contacts.addItem("Update contact", MenuCmd(self, "onUpdateContact"))
        menu_contacts.addItem("Remove contact", MenuCmd(self, "onRemoveContact"))
        menu_contacts.addItem("Manage groups", MenuCmd(self, "onManageContactGroups"))

        menu_group = MenuBar(vertical=True)
        menu_group.addItem("Discussion", MenuCmd(self, "onJoinRoom"))
        menu_group.addItem("Collective radio", MenuCmd(self, "onCollectiveRadio"))

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

        menu_help = MenuBar(vertical=True)
        menu_help.addItem("Social contract", MenuCmd(self, "onSocialContract"))
        menu_help.addItem("About", MenuCmd(self, "onAbout"))

        self.menu_settings = MenuBar(vertical=True)
        self.item_params = self.menu_settings.addItem("Parameters",
                                                      MenuCmd(self, "onParameters"))
        # XXX: temporary, will change when a full profile will be managed in SàT
        self.menu_settings.addItem("Upload avatar", MenuCmd(self, "onAvatarUpload"))

        menubar = LiberviaMenuBar()

        for _name, _icon, _menu in [('General', 'home', menu_general),
                                    ('Contacts', 'social', menu_contacts),
                                    ('Groups', 'social', menu_group),
                                    ('Games', 'games', menu_games)]:
            menubar.addItem(MenuItem(_item_tpl % (_icon, _name), True, _menu))

        _separator = MenuItem('', None)
        _separator.setStyleName('menuSeparator')
        menubar.addItem(_separator, None)

        for _name, _icon, _menu in [('Help', 'help', menu_help),
                                    ('Settings', 'settings', self.menu_settings)]:
            menubar.addItem(MenuItem(_item_tpl % (_icon, _name), True, _menu))

        self.add(menubar)

    #General menu
    def onWebWidget(self):
        web_panel = panels.WebPanel(self.host, "http://www.goffi.org")
        self.host.addWidget(web_panel)
        self.host.setSelected(web_panel)

    def onDisconnect(self):
        def confirm_cb(answer):
            if answer:
                print "disconnection"
                self.host.bridge.call('disconnect', None)
        _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to disconnect ?")
        _dialog.show()

    def onSocialContract(self):
        _frame = Frame('contrat_social.html')
        _frame.setStyleName('infoFrame')
        _dialog = dialog.GenericDialog("Contrat Social", _frame)
        _dialog.setSize('80%', '80%')
        _dialog.show()

    def onAbout(self):
        _about = HTML("""<b>Libervia</b>, a Salut &agrave; Toi project<br />
<br />
You can contact the author at <a href="mailto:goffi@goffi.org">goffi@goffi.org</a><br />
Blog available (mainly in french) at <a href="http://www.goffi.org" target="_blank">http://www.goffi.org</a><br />
Project page: <a href="http://sat.goffi.org"target="_blank">http://sat.goffi.org</a><br />
<br />
Any help welcome :)
<p style='font-size:small;text-align:center'>This project is dedicated to Roger Poisson</p>
""")
        _dialog = dialog.GenericDialog("About", _about)
        _dialog.show()

    #Contact menu
    def onAddContact(self):
        """Q&D contact addition"""
        _dialog = None
        edit = TextBox()

        def addContactCb(sender):
            if not re.match(r'^.+@.+\..+', edit.getText(), re.IGNORECASE):
                Window.alert('You must enter a valid contact JID (like "contact@%s")' % self.host._defaultDomain)
                _dialog.show()
            else:
                self.host.bridge.call('addContact', None, edit.getText(), '', _dialog.getSelectedGroups() )

        label = Label("New contact identifier (JID):")
        edit.setText('@%s' % self.host._defaultDomain)
        edit.setWidth('100%')
        _dialog = dialog.GroupSelector([label, edit], self.host.contact_panel.getGroups(), [],
                                       "Add", addContactCb)
        _dialog.setHTML('Adding contact')
        _dialog.show()

    def onUpdateContact(self):
        _dialog = None
        _contacts_list = ListBox()

        def updateContactCb(sender):
            _jid = _contacts_list.getValue(_contacts_list.getSelectedIndex())
            self.host.bridge.call('updateContact', None, _jid, '', _dialog.getSelectedGroups())

        def onContactChange(_list):
            _jid = _contacts_list.getValue(_contacts_list.getSelectedIndex())
            groups = self.host.contact_panel.getContactGroups(_jid)
            _dialog.setGroupsSelected(groups)

        for contact in self.host.contact_panel.getContacts():
            _contacts_list.addItem(contact)
        _contacts_list.addChangeListener(onContactChange)
        _jid = _contacts_list.getValue(_contacts_list.getSelectedIndex())
        _selected_groups = self.host.contact_panel.getContactGroups(_jid)
        _dialog = dialog.GroupSelector([Label('Which contact do you want to update ?'), _contacts_list],
                                       self.host.contact_panel.getGroups(), _selected_groups,
                                       "Update", updateContactCb)
        _dialog.setHTML('Updating contact')
        _dialog.show()

    def onRemoveContact(self):
        _dialog = None
        _contacts_list = ListBox()

        def secondConfirmCb(confirm):
            if confirm:
                for contact in _contacts_list.getSelectedValues():
                    self.host.bridge.call('delContact', None, contact)
            else:
                _dialog.show()

        def dialogCb(confirm):
            if confirm:
                html_list = ''.join(['<li>%s</li>' % html_sanitize(contact) for contact in _contacts_list.getSelectedValues()])
                html_body = "Are you sure to remove the following contacts from your contact list ?<ul>%s</ul>" % html_list
                dialog.ConfirmDialog(secondConfirmCb, html_body).show()

        for contact in self.host.contact_panel.getContacts():
            _contacts_list.addItem(contact)
        _dialog = dialog.GenericConfirmDialog([_contacts_list], dialogCb, "Who do you want to remove from your contacts ?")
        _dialog.show()

    def onManageContactGroups(self):
        """Open the contact groups manager."""

        def onCloseCallback():
            pass

        ContactGroupEditor(self.host, None, onCloseCallback)

    #Group menu
    def onJoinRoom(self):

        def invite(room_jid, contacts):
            for contact in contacts:
                self.host.bridge.call('inviteMUC', None, contact, room_jid)

        def join(room_jid, contacts):
            if self.host.whoami:
                nick = self.host.whoami.node
                if room_jid not in [room.bare for room in self.host.room_list]:
                    self.host.bridge.call('joinMUC', lambda room_jid: invite(room_jid, contacts), room_jid, nick)
                else:
                    self.host.getOrCreateLiberviaWidget(panels.ChatPanel, (room_jid, "group"), True, JID(room_jid).bare)
                    invite(room_jid, contacts)

        dialog.RoomAndContactsChooser(self.host, join, ok_button="Join", visible=(True, False))

    def onCollectiveRadio(self):
        def callback(room_jid, contacts):
            self.host.bridge.call('launchRadioCollective', None, contacts, room_jid)
        dialog.RoomAndContactsChooser(self.host, callback, ok_button="Choose", title="Collective Radio", visible=(False, True))

    #Game menu
    def onTarotGame(self):
        def onPlayersSelected(room_jid, other_players):
            self.host.bridge.call('launchTarotGame', None, other_players, room_jid)
        dialog.RoomAndContactsChooser(self.host, onPlayersSelected, 3, title="Tarot", title_invite="Please select 3 other players", visible=(False, True))

    def onXiangqiGame(self):
        Window.alert("A Xiangqi game is planed, but not available yet")

    #Settings menu

    def onParameters(self):
        def gotParams(xmlui):
            if not xmlui:
                return
            body = XMLUI(self.host, xmlui)
            _dialog = dialog.GenericDialog("Parameters", body, options=['NO_CLOSE'])
            body.setCloseCb(_dialog.close) 
            _dialog.setSize('80%', '80%')
            _dialog.show()
        self.host.bridge.call('getParamsUI', gotParams)

    def removeItemParams(self):
        """Remove the Parameters item from the Settings menu bar."""
        self.menu_settings.removeItem(self.item_params)

    def onAvatarUpload(self):
        body = AvatarUpload()
        _dialog = dialog.GenericDialog("Avatar upload", body, options=['NO_CLOSE'])
        body.setCloseCb(_dialog.close)
        _dialog.setSize('40%', '40%')
        _dialog.show()