view src/browser/sat_browser/contact_group.py @ 589:a5019e62c3e9 frontends_multi_profiles

browser side: big refactoring to base Libervia on QuickFrontend, first draft: /!\ not finished, partially working and highly instable - add collections module with an OrderedDict like class - SatWebFrontend inherit from QuickApp - general sat_frontends tools.jid module is used - bridge/json methods have moved to json module - UniBox is partially removed (should be totally removed before merge to trunk) - Signals are now register with the generic registerSignal method (which is called mainly in QuickFrontend) - the generic getOrCreateWidget method from QuickWidgetsManager is used instead of Libervia's specific methods - all Widget are now based more or less directly on QuickWidget - with the new QuickWidgetsManager.getWidgets method, it's no more necessary to check all widgets which are instance of a particular class - ChatPanel and related moved to chat module - MicroblogPanel and related moved to blog module - global and overcomplicated send method has been disabled: each class should manage its own sending - for consistency with other frontends, former ContactPanel has been renamed to ContactList and vice versa - for the same reason, ChatPanel has been renamed to Chat - for compatibility with QuickFrontend, a fake profile is used in several places, it is set to C.PROF_KEY_NONE (real profile is managed server side for obvious security reasons) - changed default url for web panel to SàT website, and contact address to generic SàT contact address - ContactList is based on QuickContactList, UI changes are done in update method - bride call (now json module) have been greatly improved, in particular call can be done in the same way as for other frontends (bridge.method_name(arg1, arg2, ..., callback=cb, errback=eb). Blocking method must be called like async methods due to javascript architecture - in bridge calls, a callback can now exists without errback - hard reload on BridgeSignals remote error has been disabled, a better option should be implemented - use of constants where that make sens, some style improvments - avatars are temporarily disabled - lot of code disabled, will be fixed or removed before merge - various other changes, check diff for more details server side: manage remote exception on getEntityData, removed getProfileJid call, added getWaitingConf, added getRoomsSubjects
author Goffi <goffi@goffi.org>
date Sat, 24 Jan 2015 01:45:39 +0100
parents 50b286866739
children 32dbbc941123
line wrap: on
line source

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

# Libervia: a Salut à Toi frontend
# Copyright (C) 2013, 2014 Adrien Cossa <souliane@mailoo.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/>.

from pyjamas.ui.FlexTable import FlexTable
from pyjamas.ui.DockPanel import DockPanel
from pyjamas.Timer import Timer
from pyjamas.ui.Button import Button
from pyjamas.ui.HorizontalPanel import HorizontalPanel
from pyjamas.ui.VerticalPanel import VerticalPanel
from pyjamas.ui.DialogBox import DialogBox
from pyjamas.ui import HasAlignment

import dialog
import list_manager
import contact_list


class ContactGroupManager(list_manager.ListManager):
    """A manager for sub-panels to assign contacts to each group."""

    def __init__(self, parent, keys_dict, contacts, offsets, style):
        list_manager.ListManager.__init__(self, parent, keys_dict, contacts, offsets, style)
        self.registerPopupMenuPanel(entries={"Remove group": {}},
                                    callback=lambda sender, key: Timer(5, lambda timer: self.removeContactKey(sender, key)))

    def removeContactKey(self, sender, key):
        key = sender.getText()

        def confirm_cb(answer):
            if answer:
                list_manager.ListManager.removeContactKey(self, key)
                self._parent.removeKeyFromAddGroupPanel(key)

        _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to delete the group '%s'?" % key)
        _dialog.show()

    def removeFromRemainingList(self, contacts):
        list_manager.ListManager.removeFromRemainingList(self, contacts)
        self._parent.updateContactList(contacts=contacts)

    def addToRemainingList(self, contacts, ignore_key=None):
        list_manager.ListManager.addToRemainingList(self, contacts, ignore_key)
        self._parent.updateContactList(contacts=contacts)


class ContactGroupEditor(DockPanel):
    """Panel for the contact groups manager."""

    def __init__(self, host, parent=None, onCloseCallback=None):
        DockPanel.__init__(self)
        self.host = host

        # eventually display in a popup
        if parent is None:
            parent = DialogBox(autoHide=False, centered=True)
            parent.setHTML("Manage contact groups")
        self._parent = parent
        self._on_close_callback = onCloseCallback
        self.all_contacts = self.host.contact_panel.getContacts()

        groups_list = self.host.contact_panel.groups.keys()
        groups_list.sort()

        self.add_group_panel = self.getAddGroupPanel(groups_list)
        south_panel = self.getCloseSaveButtons()
        center_panel = self.getContactGroupManager(groups_list)
        east_panel = self.getContactList()

        self.add(self.add_group_panel, DockPanel.CENTER)
        self.add(east_panel, DockPanel.EAST)
        self.add(center_panel, DockPanel.NORTH)
        self.add(south_panel, DockPanel.SOUTH)

        self.setCellHorizontalAlignment(center_panel, HasAlignment.ALIGN_LEFT)
        self.setCellVerticalAlignment(center_panel, HasAlignment.ALIGN_TOP)
        self.setCellHorizontalAlignment(east_panel, HasAlignment.ALIGN_RIGHT)
        self.setCellVerticalAlignment(east_panel, HasAlignment.ALIGN_TOP)
        self.setCellVerticalAlignment(self.add_group_panel, HasAlignment.ALIGN_BOTTOM)
        self.setCellHorizontalAlignment(self.add_group_panel, HasAlignment.ALIGN_LEFT)
        self.setCellVerticalAlignment(south_panel, HasAlignment.ALIGN_BOTTOM)
        self.setCellHorizontalAlignment(south_panel, HasAlignment.ALIGN_CENTER)

        # need to be done after the contact list has been initialized
        self.groups.setContacts(self.host.contact_panel.groups)
        self.toggleContacts(showAll=True)

        # Hide the contacts list from the main panel to not confuse the user
        self.restore_contact_panel = False
        if self.host.contact_panel.getVisible():
            self.restore_contact_panel = True
            self.host.panel._contactsSwitch()

        parent.add(self)
        parent.setVisible(True)
        if isinstance(parent, DialogBox):
            parent.center()

    def getContactGroupManager(self, groups_list):
        """Set the list manager for the groups"""
        flex_table = FlexTable(len(groups_list), 2)
        flex_table.addStyleName('contactGroupEditor')
        # overwrite the default style which has been set for rich text editor
        style = {
           "keyItem": "group",
           "popupMenuItem": "popupMenuItem",
           "removeButton": "contactGroupRemoveButton",
           "buttonCell": "contactGroupButtonCell",
           "keyPanel": "contactGroupPanel"
        }
        self.groups = ContactGroupManager(flex_table, groups_list, self.all_contacts, style=style)
        self.groups.createWidgets()  # widgets are automatically added to FlexTable
        # FIXME: clean that part which is dangerous
        flex_table.updateContactList = self.updateContactList
        flex_table.removeKeyFromAddGroupPanel = self.add_group_panel.groups.remove
        return flex_table

    def getAddGroupPanel(self, groups_list):
        """Add the 'Add group' panel to the FlexTable"""

        def add_group_cb(text):
            self.groups.addContactKey(text)
            self.add_group_panel.textbox.setFocus(True)

        add_group_panel = dialog.AddGroupPanel(groups_list, add_group_cb)
        add_group_panel.addStyleName("addContactGroupPanel")
        return add_group_panel

    def getCloseSaveButtons(self):
        """Add the buttons to close the dialog / save the groups"""
        buttons = HorizontalPanel()
        buttons.addStyleName("marginAuto")
        buttons.add(Button("Save", listener=self.closeAndSave))
        buttons.add(Button("Cancel", listener=self.cancelWithoutSaving))
        return buttons

    def getContactList(self):
        """Add the contact list to the DockPanel"""
        self.toggle = Button("", self.toggleContacts)
        self.toggle.addStyleName("toggleAssignedContacts")
        self.contacts = contact_list.BaseContactPanel(self.host)
        for contact_ in self.all_contacts:
            self.contacts.add(contact_)
        contact_panel = VerticalPanel()
        contact_panel.add(self.toggle)
        contact_panel.add(self.contacts)
        return contact_panel

    def toggleContacts(self, sender=None, showAll=None):
        """Callback for the toggle button"""
        if sender is None:
            sender = self.toggle
        sender.showAll = showAll if showAll is not None else not sender.showAll
        if sender.showAll:
            sender.setText("Hide assigned")
        else:
            sender.setText("Show assigned")
        self.updateContactList(sender)

    def updateContactList(self, sender=None, contacts=None):
        """Update the contact list regarding the toggle button"""
        if not hasattr(self, "toggle") or not hasattr(self.toggle, "showAll"):
            return
        sender = self.toggle
        if contacts is not None:
            if not isinstance(contacts, list):
                contacts = [contacts]
            for contact_ in contacts:
                if contact_ not in self.all_contacts:
                    contacts.remove(contact_)
        else:
            contacts = self.all_contacts
        for contact_ in contacts:
            if sender.showAll:
                self.contacts.getContactBox(contact_).setVisible(True)
            else:
                if contact_ in self.groups.remaining_list:
                    self.contacts.getContactBox(contact_).setVisible(True)
                else:
                    self.contacts.getContactBox(contact_).setVisible(False)

    def __close(self):
        """Remove the widget from parent or close the popup."""
        if isinstance(self._parent, DialogBox):
            self._parent.hide()
        self._parent.remove(self)
        if self._on_close_callback is not None:
            self._on_close_callback()
        if self.restore_contact_panel:
            self.host.panel._contactsSwitch()

    def cancelWithoutSaving(self):
        """Ask for confirmation before closing the dialog."""
        def confirm_cb(answer):
            if answer:
                self.__close()

        _dialog = dialog.ConfirmDialog(confirm_cb, text="Do you really want to cancel without saving?")
        _dialog.show()

    def closeAndSave(self):
        """Call bridge methods to save the changes and close the dialog"""
        map_ = {}
        for contact_ in self.all_contacts:
            map_[contact_] = set()
        contacts = self.groups.getContacts()
        for group in contacts.keys():
            for contact_ in contacts[group]:
                try:
                    map_[contact_].add(group)
                except KeyError:
                    dialog.InfoDialog("Invalid contact",
                           "The contact '%s' is not your contact list but it has been assigned to the group '%s'." % (contact_, group) +
                           "Your changes could not be saved: please check your assignments and save again.", Width="400px").center()
                    return
        for contact_ in map_.keys():
            groups = map_[contact_]
            current_groups = self.host.contact_panel.getContactGroups(contact_)
            if groups != current_groups:
                self.host.bridge.call('updateContact', None, contact_, '', list(groups))
        self.__close()