def Enable(self):
        """
        All real actions involved in initializing the service should
        be defined here, not in the constructor.  This includes
        e.g. opening sockets, collecting data from directories, etc.
        """
        set_solipsis_dir(self.service_api.GetDirectory())
        # init windows
        main_window = self.service_api.GetMainWindow()
        options = {}
        options["standalone"] = False
        # profile_frame created at once since linked to facade & thus
        # needed for all actions (get_blog, get_files), even if not
        # displayed right at beginning
        self.profile_frame = ProfileFrame(options, main_window, -1, "", plugin=self)
        self.node_id = self.service_api.GetNode().pseudo
        # create views & doc
        self.facade.load_profile(os.path.join(PROFILE_DIR, PROFILE_FILE), self.profile_frame)
        self.facade.refresh_html_preview()
        # Set up main GUI hooks
        menu = wx.Menu()
        for action, method in self.MAIN_ACTION.iteritems():
            item_id = wx.NewId()
            menu.Append(item_id, _(action))

            def _clicked(event):
                """call profile from main menu"""
                method()

            wx.EVT_MENU(main_window, item_id, _clicked)
        self.service_api.SetMenu(self.GetTitle(), menu)
        # launch network
        self.network.start_listening()
class Plugin(ServicePlugin):
    """This the working class for services plugins."""

    def Init(self):
        """
        This method does any kind of concrete initialization stuff,
        rather than doing it in __init__.  Especially, the service_api
        must *not* be used in __init__, since all data relating to the
        plugin may not have been initialized on the API side.
        """
        # TODO: smartly discover our own address IP
        # (this is where duplicated code starts to appear...)
        self.host = socket.gethostbyname(socket.gethostname())
        self.port = random.randrange(7000, 7100)
        self.network = NetworkManager(self.host, random.randrange(7100, 7200), self.service_api, get_facade())
        # init facade. Views will be initialized in Enable
        # (views depend on graphical mode)
        self.facade = get_facade()
        self.profile_frame = None
        self.node_id = None
        # declare actions
        self.MAIN_ACTION = {"Modify ...": self.show_profile}
        self.POINT_ACTIONS = {
            "Get profile...": self.get_profile,
            "Get blog...": self.get_blog_file,
            "Get files...": self.select_files,
        }

    # Service setup

    # FIXME: need to abstract action layer of plugins so that it does
    # not depend on navigator mode (in our case: netclient or
    # wxclient)
    def EnableBasic(self):
        """enable non graphic part"""
        set_solipsis_dir(self.service_api.GetDirectory())
        # TODO: expose interface of facade to netClient
        self.facade.load_profile(os.path.join(PROFILE_DIR, PROFILE_FILE))
        # launch network
        self.network.start_listening()

    def Enable(self):
        """
        All real actions involved in initializing the service should
        be defined here, not in the constructor.  This includes
        e.g. opening sockets, collecting data from directories, etc.
        """
        set_solipsis_dir(self.service_api.GetDirectory())
        # init windows
        main_window = self.service_api.GetMainWindow()
        options = {}
        options["standalone"] = False
        # profile_frame created at once since linked to facade & thus
        # needed for all actions (get_blog, get_files), even if not
        # displayed right at beginning
        self.profile_frame = ProfileFrame(options, main_window, -1, "", plugin=self)
        self.node_id = self.service_api.GetNode().pseudo
        # create views & doc
        self.facade.load_profile(os.path.join(PROFILE_DIR, PROFILE_FILE), self.profile_frame)
        self.facade.refresh_html_preview()
        # Set up main GUI hooks
        menu = wx.Menu()
        for action, method in self.MAIN_ACTION.iteritems():
            item_id = wx.NewId()
            menu.Append(item_id, _(action))

            def _clicked(event):
                """call profile from main menu"""
                method()

            wx.EVT_MENU(main_window, item_id, _clicked)
        self.service_api.SetMenu(self.GetTitle(), menu)
        # launch network
        self.network.start_listening()

    def Disable(self):
        """It is called when the user chooses to disable the service."""
        self.facade.save_profile()
        self.network.stop_listening()

    # Service methods
    def show_profile(self):
        """display profile once loaded"""
        if self.profile_frame:
            self.profile_frame.Show()
            self.profile_frame.blog_tab.on_update()

    # Transfer methods
    def _on_new_profile(self, document, peer_id):
        """store and display file object corresponding to profile"""
        print "downloaded profile", document.get_pseudo(), peer_id
        self.facade.fill_data((peer_id, document))
        if self.profile_frame:
            self.profile_frame.display_profile(peer_id)

    def _on_new_blog(self, blog, peer_id):
        """store and display file object corresponding to blog"""
        self.facade.fill_blog((peer_id, blog))

    def _on_shared_files(self, files, peer_id):
        """store and display file object corresponding to blog"""
        self.facade.fill_shared_files((peer_id, files))

    def _on_all_files(self):
        """store and display file object corresponding to blog"""
        if self.profile_frame:

            def display_message():
                dlg = wx.MessageDialog(
                    self.service_api.GetMainWindow(),
                    "Download complete!",
                    "No more file to download",
                    wx.OK | wx.ICON_INFORMATION,
                )
                dlg.ShowModal()
                dlg.Destroy()

            wx.CallAfter(display_message)
        else:
            print "No more file to download"

    def get_profile(self, peer_id):
        """request downwload of profile"""
        deferred = self.network.get_profile(peer_id)
        deferred and deferred.addCallback(self._on_new_profile, peer_id)

    def get_blog_file(self, peer_id):
        """request downwload of blog"""
        deferred = self.network.get_blog_file(peer_id)
        deferred and deferred.addCallback(self._on_new_blog, peer_id)

    def get_files(self, peer_id, file_names):
        """request downwload of given files"""
        deferred = self.network.get_files(peer_id, file_names, self._on_all_files)
        deferred and deferred.addCallback(lambda file_name: sys.stdout.write("%s downloaded\n" % file_name))

    def select_files(self, peer_id):
        """request downwload of list of shared files"""
        deferred = self.network.get_shared_files(peer_id)
        deferred and deferred.addCallback(self._on_shared_files, peer_id)

    # Service description methods
    def GetTitle(self):
        """Returns a short title of the plugin."""
        return _("Profile")

    def GetDescription(self):
        """Returns a short description of the plugin. """
        return _("Manage personal information and share it with people")

    def GetActions(self):
        """Returns an array of strings advertising possible actions
        with all peers, or None if no such action is allowed.
        """
        return [_(desc) for desc in self.MAIN_ACTION]

    def GetPointToPointActions(self):
        """Returns an array of strings advertising point-to-point
        actions with another peer, or None if point-to-point actions
        are not allowed.
        """
        return [_(desc) for desc in self.POINT_ACTIONS]

    def DescribeService(self, service):
        """
        Fills the Service object used to describe the service to other peers.
        """
        service.address = "%s:%d" % (self.host, self.port)

    # UI event responses
    def DoAction(self, it):
        """Called when the general action is invoked, if available."""
        self.show_profile()

    def DoPointToPointAction(self, it, peer):
        """Called when a point-to-point action is invoked, if available."""
        if self.facade.is_activated():
            # retreive corect method
            actions = self.POINT_ACTIONS.values()
            # call method on peer
            actions[it](peer.id_)
        # service not activated, do nothing
        else:
            print "service not activated"

    # Peer management
    # FIXME: reactivate when efficient
    def NewPeer(self, peer, service):
        """delegate to network"""
        if self.facade.is_activated():
            self.network.on_new_peer(peer, service)

    def ChangedPeer(self, peer, service):
        """delegate to network"""
        if self.facade.is_activated():
            self.network.on_change_peer(peer, service)

    def LostPeer(self, peer_id):
        """delegate to network"""
        if self.facade.is_activated():
            self.network.on_lost_peer(peer_id)
            self.facade.set_connected(peer_id, False)

    def GotServiceData(self, peer_id, data):
        """delegate to network"""
        if self.facade.is_activated():
            self.network.on_service_data(peer_id, data)

    def ChangedNode(self, node):
        """need to update node_id"""
        # FIXME new node_id ? what consequence on network? cache?
        pass