diff src/browser/menu.py @ 449:981ed669d3b3

/!\ reorganize all the file hierarchy, move the code and launching script to src: - browser_side --> src/browser - public --> src/browser_side/public - libervia.py --> src/browser/libervia_main.py - libervia_server --> src/server - libervia_server/libervia.sh --> src/libervia.sh - twisted --> src/twisted - new module src/common - split constants.py in 3 files: - src/common/constants.py - src/browser/constants.py - src/server/constants.py - output --> html (generated by pyjsbuild during the installation) - new option/parameter "data_dir" (-d) to indicates the directory containing html and server_css - setup.py installs libervia to the following paths: - src/common --> <LIB>/libervia/common - src/server --> <LIB>/libervia/server - src/twisted --> <LIB>/twisted - html --> <SHARE>/libervia/html - server_side --> <SHARE>libervia/server_side - LIBERVIA_INSTALL environment variable takes 2 new options with prompt confirmation: - clean: remove previous installation directories - purge: remove building and previous installation directories You may need to update your sat.conf and/or launching script to update the following options/parameters: - ssl_certificate - data_dir
author souliane <souliane@mailoo.org>
date Tue, 20 May 2014 06:41:16 +0200
parents browser_side/menu.py@d52f529a6d42
children bea9788f3170
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/browser/menu.py	Tue May 20 06:41:16 2014 +0200
@@ -0,0 +1,271 @@
+#!/usr/bin/python
+# -*- coding: utf-8 -*-
+
+# Libervia: a Salut à Toi frontend
+# Copyright (C) 2011, 2012, 2013, 2014 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 sat.core.log import getLogger
+log = getLogger(__name__)
+
+from sat.core.i18n import _
+
+from pyjamas.ui.SimplePanel import SimplePanel
+from pyjamas.ui.MenuBar import MenuBar
+from pyjamas.ui.MenuItem import MenuItem
+from pyjamas.ui.HTML import HTML
+from pyjamas.ui.Frame import Frame
+from pyjamas import Window
+
+from jid import JID
+
+from file_tools import FileUploadPanel
+from xmlui import XMLUI
+import panels
+import dialog
+from contact_group import ContactGroupEditor
+
+
+class MenuCmd:
+
+    def __init__(self, object_, handler):
+        self._object = object_
+        self._handler = handler
+
+    def execute(self):
+        handler = getattr(self._object, self._handler)
+        handler()
+
+
+class PluginMenuCmd:
+
+    def __init__(self, host, action_id):
+        self.host = host
+        self.action_id = action_id
+
+    def execute(self):
+        self.host.launchAction(self.action_id, None)
+
+
+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(FileUploadPanel):
+    def __init__(self):
+        texts = {'ok_button': 'Upload avatar',
+                 'body': '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.',
+                 'errback': "Can't open image... did you actually submit an image?",
+                 'body_errback': 'Please select another image file.',
+                 'callback': "Your new profile picture has been set!"}
+        FileUploadPanel.__init__(self, 'upload_avatar', 'avatar_path', 2, texts)
+
+
+class Menu(SimplePanel):
+
+    def __init__(self, host):
+        self.host = host
+        SimplePanel.__init__(self)
+        self.setStyleName('menuContainer')
+
+    def createMenus(self, add_menus):
+        _item_tpl = "<img src='media/icons/menu/%s_menu_red.png' />%s"
+        menus_dict = {}
+        menus_order = []
+
+        def addMenu(menu_name, menu_name_i18n, item_name_i18n, icon, menu_cmd):
+            """ add a menu to menu_dict """
+            log.info("addMenu: %s %s %s %s %s" % (menu_name, menu_name_i18n, item_name_i18n, icon, menu_cmd))
+            try:
+                menu_bar = menus_dict[menu_name]
+            except KeyError:
+                menu_bar = menus_dict[menu_name] = MenuBar(vertical=True)
+                menus_order.append((menu_name, menu_name_i18n, icon))
+            if item_name_i18n and menu_cmd:
+                menu_bar.addItem(item_name_i18n, menu_cmd)
+
+        addMenu("General", _("General"), _("Web widget"), 'home', MenuCmd(self, "onWebWidget"))
+        addMenu("General", _("General"), _("Disconnect"), 'home', MenuCmd(self, "onDisconnect"))
+        addMenu("Contacts", _("Contacts"), None, 'social', None)
+        addMenu("Groups", _("Groups"), _("Discussion"), 'social', MenuCmd(self, "onJoinRoom"))
+        addMenu("Groups", _("Groups"), _("Collective radio"), 'social', MenuCmd(self, "onCollectiveRadio"))
+        addMenu("Games", _("Games"), _("Tarot"), 'games', MenuCmd(self, "onTarotGame"))
+        addMenu("Games", _("Games"), _("Xiangqi"), 'games', MenuCmd(self, "onXiangqiGame"))
+
+        # additional menus
+        for action_id, type_, path, path_i18n in add_menus:
+            if not path:
+                log.warning("skipping menu without path")
+                continue
+            if len(path) != len(path_i18n):
+                log.error("inconsistency between menu paths")
+                continue
+            menu_name = path[0]
+            menu_name_i18n = path_i18n[0]
+            item_name = path[1:]
+            if not item_name:
+                log.warning("skipping menu with a path of lenght 1 [%s]" % path[0])
+                continue
+            item_name_i18n = ' | '.join(path_i18n[1:])
+            addMenu(menu_name, menu_name_i18n, item_name_i18n, 'plugins', PluginMenuCmd(self.host, action_id))
+
+        # menu items that should be displayed after the automatically added ones
+        addMenu("Contacts", _("Contacts"), _("Manage groups"), 'social', MenuCmd(self, "onManageContactGroups"))
+
+        menus_order.append(None)  # we add separator
+
+        addMenu("Help", _("Help"), _("Social contract"), 'help', MenuCmd(self, "onSocialContract"))
+        addMenu("Help", _("Help"), _("About"), 'help', MenuCmd(self, "onAbout"))
+        addMenu("Settings", _("Settings"), _("Account"), 'settings', MenuCmd(self, "onAccount"))
+        addMenu("Settings", _("Settings"), _("Parameters"), 'settings', MenuCmd(self, "onParameters"))
+
+        # XXX: temporary, will change when a full profile will be managed in SàT
+        addMenu("Settings", _("Settings"), _("Upload avatar"), 'settings', MenuCmd(self, "onAvatarUpload"))
+
+        menubar = LiberviaMenuBar()
+
+        for menu_data in menus_order:
+            if menu_data is None:
+                _separator = MenuItem('', None)
+                _separator.setStyleName('menuSeparator')
+                menubar.addItem(_separator, None)
+            else:
+                menu_name, menu_name_i18n, icon = menu_data
+                menubar.addItem(MenuItem(_item_tpl % (icon, menu_name_i18n), True, menus_dict[menu_name]))
+
+        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:
+                log.info("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 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 onAccount(self):
+        def gotUI(xmlui):
+            if not xmlui:
+                return
+            body = XMLUI(self.host, xmlui)
+            _dialog = dialog.GenericDialog("Manage your XMPP account", body, options=['NO_CLOSE'])
+            body.setCloseCb(_dialog.close)
+            _dialog.show()
+        self.host.bridge.call('getAccountDialogUI', gotUI)
+
+    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.setWidth('40%')
+        _dialog.show()