[Dicson] ability to install from zip / uninstall plugin. Fixes #5906
This commit is contained in:
		
							parent
							
								
									cfac956598
								
							
						
					
					
						commit
						096b8f3d91
					
				
					 6 changed files with 249 additions and 35 deletions
				
			
		| 
						 | 
				
			
			@ -245,10 +245,47 @@
 | 
			
		|||
                        <property name="visible">True</property>
 | 
			
		||||
                        <property name="spacing">5</property>
 | 
			
		||||
                        <property name="layout_style">end</property>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkButton" id="install_plugin_button">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="receives_default">True</property>
 | 
			
		||||
                            <signal name="clicked" handler="on_install_plugin_button_clicked"/>
 | 
			
		||||
                            <child>
 | 
			
		||||
                              <object class="GtkHBox" id="hbox13">
 | 
			
		||||
                                <property name="visible">True</property>
 | 
			
		||||
                                <child>
 | 
			
		||||
                                  <object class="GtkImage" id="image3">
 | 
			
		||||
                                    <property name="visible">True</property>
 | 
			
		||||
                                    <property name="stock">gtk-apply</property>
 | 
			
		||||
                                  </object>
 | 
			
		||||
                                  <packing>
 | 
			
		||||
                                    <property name="position">0</property>
 | 
			
		||||
                                  </packing>
 | 
			
		||||
                                </child>
 | 
			
		||||
                                <child>
 | 
			
		||||
                                  <object class="GtkLabel" id="install_plugin_button_label">
 | 
			
		||||
                                    <property name="visible">True</property>
 | 
			
		||||
                                    <property name="xalign">0</property>
 | 
			
		||||
                                    <property name="label" translatable="yes">Install</property>
 | 
			
		||||
                                  </object>
 | 
			
		||||
                                  <packing>
 | 
			
		||||
                                    <property name="position">1</property>
 | 
			
		||||
                                  </packing>
 | 
			
		||||
                                </child>
 | 
			
		||||
                              </object>
 | 
			
		||||
                            </child>
 | 
			
		||||
                          </object>
 | 
			
		||||
                          <packing>
 | 
			
		||||
                            <property name="expand">False</property>
 | 
			
		||||
                            <property name="fill">False</property>
 | 
			
		||||
                            <property name="position">0</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <child>
 | 
			
		||||
                          <object class="GtkButton" id="uninstall_plugin_button">
 | 
			
		||||
                            <property name="visible">True</property>
 | 
			
		||||
                            <property name="can_focus">True</property>
 | 
			
		||||
                            <property name="can_focus">False</property>
 | 
			
		||||
                            <property name="receives_default">True</property>
 | 
			
		||||
                            <signal name="clicked" handler="on_uninstall_plugin_button_clicked"/>
 | 
			
		||||
                            <child>
 | 
			
		||||
| 
						 | 
				
			
			@ -266,6 +303,7 @@
 | 
			
		|||
                                <child>
 | 
			
		||||
                                  <object class="GtkLabel" id="uninstall_plugin_button_label">
 | 
			
		||||
                                    <property name="visible">True</property>
 | 
			
		||||
                                    <property name="xalign">0</property>
 | 
			
		||||
                                    <property name="label" translatable="yes">Uninstall</property>
 | 
			
		||||
                                  </object>
 | 
			
		||||
                                  <packing>
 | 
			
		||||
| 
						 | 
				
			
			@ -278,7 +316,7 @@
 | 
			
		|||
                          <packing>
 | 
			
		||||
                            <property name="expand">False</property>
 | 
			
		||||
                            <property name="fill">False</property>
 | 
			
		||||
                            <property name="position">0</property>
 | 
			
		||||
                            <property name="position">1</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                        <child>
 | 
			
		||||
| 
						 | 
				
			
			@ -302,6 +340,7 @@
 | 
			
		|||
                                <child>
 | 
			
		||||
                                  <object class="GtkLabel" id="configure_plugin_button_label">
 | 
			
		||||
                                    <property name="visible">True</property>
 | 
			
		||||
                                    <property name="xalign">0</property>
 | 
			
		||||
                                    <property name="label" translatable="yes">Configure</property>
 | 
			
		||||
                                  </object>
 | 
			
		||||
                                  <packing>
 | 
			
		||||
| 
						 | 
				
			
			@ -314,7 +353,7 @@
 | 
			
		|||
                          <packing>
 | 
			
		||||
                            <property name="expand">False</property>
 | 
			
		||||
                            <property name="fill">False</property>
 | 
			
		||||
                            <property name="position">1</property>
 | 
			
		||||
                            <property name="position">2</property>
 | 
			
		||||
                          </packing>
 | 
			
		||||
                        </child>
 | 
			
		||||
                      </object>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -61,7 +61,7 @@ from common.connection_handlers_events import *
 | 
			
		|||
from common import ged
 | 
			
		||||
from common import nec
 | 
			
		||||
from common.nec import NetworkEvent
 | 
			
		||||
from plugins import GajimPlugin
 | 
			
		||||
 | 
			
		||||
if gajim.HAVE_FARSIGHT:
 | 
			
		||||
    from common.jingle import ConnectionJingle
 | 
			
		||||
else:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -131,3 +131,15 @@ class GajimGeneralException(Exception):
 | 
			
		|||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.text
 | 
			
		||||
 | 
			
		||||
class PluginsystemError(Exception):
 | 
			
		||||
    """
 | 
			
		||||
    Error in the pluginsystem
 | 
			
		||||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, text=''):
 | 
			
		||||
        Exception.__init__(self)
 | 
			
		||||
        self.text = text
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return self.text
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1349,11 +1349,11 @@ class FileChooserDialog(gtk.FileChooserDialog):
 | 
			
		|||
    Non-blocking FileChooser Dialog around gtk.FileChooserDialog
 | 
			
		||||
    """
 | 
			
		||||
    def __init__(self, title_text, action, buttons, default_response,
 | 
			
		||||
                             select_multiple = False, current_folder = None, on_response_ok = None,
 | 
			
		||||
                             on_response_cancel = None):
 | 
			
		||||
    select_multiple=False, current_folder=None, on_response_ok=None,
 | 
			
		||||
    on_response_cancel=None):
 | 
			
		||||
 | 
			
		||||
        gtk.FileChooserDialog.__init__(self, title=title_text, action=action,
 | 
			
		||||
                                                                   buttons=buttons)
 | 
			
		||||
            buttons=buttons)
 | 
			
		||||
 | 
			
		||||
        self.set_default_response(default_response)
 | 
			
		||||
        self.set_select_multiple(select_multiple)
 | 
			
		||||
| 
						 | 
				
			
			@ -1468,8 +1468,8 @@ class WarningDialog(HigDialog):
 | 
			
		|||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, pritext, sectext=''):
 | 
			
		||||
        HigDialog.__init__( self, None,
 | 
			
		||||
                                                gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, pritext, sectext)
 | 
			
		||||
        HigDialog.__init__(self, None, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK,
 | 
			
		||||
            pritext, sectext)
 | 
			
		||||
        self.set_modal(False)
 | 
			
		||||
        if hasattr(gajim.interface, 'roster') and gajim.interface.roster:
 | 
			
		||||
            self.set_transient_for(gajim.interface.roster.window)
 | 
			
		||||
| 
						 | 
				
			
			@ -1505,13 +1505,12 @@ class YesNoDialog(HigDialog):
 | 
			
		|||
    """
 | 
			
		||||
 | 
			
		||||
    def __init__(self, pritext, sectext='', checktext='', on_response_yes=None,
 | 
			
		||||
                    on_response_no=None):
 | 
			
		||||
    on_response_no=None):
 | 
			
		||||
        self.user_response_yes = on_response_yes
 | 
			
		||||
        self.user_response_no = on_response_no
 | 
			
		||||
        HigDialog.__init__( self, None,
 | 
			
		||||
                                                gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO, pritext, sectext,
 | 
			
		||||
                                                on_response_yes=self.on_response_yes,
 | 
			
		||||
                                                on_response_no=self.on_response_no)
 | 
			
		||||
        HigDialog.__init__(self, None, gtk.MESSAGE_QUESTION, gtk.BUTTONS_YES_NO,
 | 
			
		||||
            pritext, sectext, on_response_yes=self.on_response_yes,
 | 
			
		||||
            on_response_no=self.on_response_no)
 | 
			
		||||
 | 
			
		||||
        if checktext:
 | 
			
		||||
            self.checkbutton = gtk.CheckButton(checktext)
 | 
			
		||||
| 
						 | 
				
			
			@ -4442,6 +4441,52 @@ class AvatarChooserDialog(ImageChooserDialog):
 | 
			
		|||
        else:
 | 
			
		||||
            self.response_clear(widget)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ArchiveChooserDialog(FileChooserDialog):
 | 
			
		||||
    def __init__(self, on_response_ok=None, on_response_cancel=None):
 | 
			
		||||
 | 
			
		||||
        def on_ok(widget, callback):
 | 
			
		||||
            '''check if file exists and call callback'''
 | 
			
		||||
            path_to_file = self.get_filename()
 | 
			
		||||
            if not path_to_file:
 | 
			
		||||
                return
 | 
			
		||||
            path_to_file = gtkgui_helpers.decode_filechooser_file_paths(
 | 
			
		||||
                (path_to_file,))[0]
 | 
			
		||||
            if os.path.exists(path_to_file):
 | 
			
		||||
                if isinstance(callback, tuple):
 | 
			
		||||
                    callback[0](path_to_file, *callback[1:])
 | 
			
		||||
                else:
 | 
			
		||||
                    callback(path_to_file)
 | 
			
		||||
            self.destroy()
 | 
			
		||||
 | 
			
		||||
        path = helpers.get_documents_path()
 | 
			
		||||
 | 
			
		||||
        FileChooserDialog.__init__(self,
 | 
			
		||||
            title_text=_('Choose Archive'),
 | 
			
		||||
            action=gtk.FILE_CHOOSER_ACTION_OPEN,
 | 
			
		||||
            buttons=(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL,
 | 
			
		||||
                gtk.STOCK_OPEN, gtk.RESPONSE_OK),
 | 
			
		||||
            default_response=gtk.RESPONSE_OK,
 | 
			
		||||
            current_folder=path,
 | 
			
		||||
            on_response_ok=(on_ok, on_response_ok),
 | 
			
		||||
            on_response_cancel=on_response_cancel)
 | 
			
		||||
 | 
			
		||||
        if on_response_cancel:
 | 
			
		||||
            self.connect('destroy', on_response_cancel)
 | 
			
		||||
 | 
			
		||||
        filter_ = gtk.FileFilter()
 | 
			
		||||
        filter_.set_name(_('All files'))
 | 
			
		||||
        filter_.add_pattern('*')
 | 
			
		||||
        self.add_filter(filter_)
 | 
			
		||||
 | 
			
		||||
        filter_ = gtk.FileFilter()
 | 
			
		||||
        filter_.set_name(_('Zip files'))
 | 
			
		||||
        filter_.add_pattern('*.zip')
 | 
			
		||||
 | 
			
		||||
        self.add_filter(filter_)
 | 
			
		||||
        self.set_filter(filter_)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class AddSpecialNotificationDialog:
 | 
			
		||||
    def __init__(self, jid):
 | 
			
		||||
        """
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,9 +30,10 @@ import pango
 | 
			
		|||
import gtk, gobject
 | 
			
		||||
 | 
			
		||||
import gtkgui_helpers
 | 
			
		||||
import dialogs
 | 
			
		||||
from common import gajim
 | 
			
		||||
 | 
			
		||||
from plugins.helpers import log_calls, log
 | 
			
		||||
from common.exceptions import PluginsystemError
 | 
			
		||||
 | 
			
		||||
class PluginsWindow(object):
 | 
			
		||||
    '''Class for Plugins window'''
 | 
			
		||||
| 
						 | 
				
			
			@ -44,15 +45,11 @@ class PluginsWindow(object):
 | 
			
		|||
        self.window = self.xml.get_object('plugins_window')
 | 
			
		||||
        self.window.set_transient_for(gajim.interface.roster.window)
 | 
			
		||||
 | 
			
		||||
        widgets_to_extract = ('plugins_notebook',
 | 
			
		||||
                                                 'plugin_name_label',
 | 
			
		||||
                                                 'plugin_version_label',
 | 
			
		||||
                                                 'plugin_authors_label',
 | 
			
		||||
                                                 'plugin_homepage_linkbutton',
 | 
			
		||||
                                                 'plugin_description_textview',
 | 
			
		||||
                                                 'uninstall_plugin_button',
 | 
			
		||||
                                                 'configure_plugin_button',
 | 
			
		||||
                                                 'installed_plugins_treeview')
 | 
			
		||||
        widgets_to_extract = ('plugins_notebook', 'plugin_name_label',
 | 
			
		||||
            'plugin_version_label', 'plugin_authors_label',
 | 
			
		||||
            'plugin_homepage_linkbutton', 'plugin_description_textview',
 | 
			
		||||
            'uninstall_plugin_button', 'configure_plugin_button',
 | 
			
		||||
            'installed_plugins_treeview')
 | 
			
		||||
 | 
			
		||||
        for widget_name in widgets_to_extract:
 | 
			
		||||
            setattr(self, widget_name, self.xml.get_object(widget_name))
 | 
			
		||||
| 
						 | 
				
			
			@ -62,8 +59,7 @@ class PluginsWindow(object):
 | 
			
		|||
        self.plugin_name_label.set_attributes(attr_list)
 | 
			
		||||
 | 
			
		||||
        self.installed_plugins_model = gtk.ListStore(gobject.TYPE_PYOBJECT,
 | 
			
		||||
                                                                                                 gobject.TYPE_STRING,
 | 
			
		||||
                                                                                                 gobject.TYPE_BOOLEAN)
 | 
			
		||||
            gobject.TYPE_STRING, gobject.TYPE_BOOLEAN)
 | 
			
		||||
        self.installed_plugins_treeview.set_model(self.installed_plugins_model)
 | 
			
		||||
 | 
			
		||||
        renderer = gtk.CellRendererText()
 | 
			
		||||
| 
						 | 
				
			
			@ -79,7 +75,7 @@ class PluginsWindow(object):
 | 
			
		|||
        # connect signal for selection change
 | 
			
		||||
        selection = self.installed_plugins_treeview.get_selection()
 | 
			
		||||
        selection.connect('changed',
 | 
			
		||||
                                          self.installed_plugins_treeview_selection_changed)
 | 
			
		||||
            self.installed_plugins_treeview_selection_changed)
 | 
			
		||||
        selection.set_mode(gtk.SELECTION_SINGLE)
 | 
			
		||||
 | 
			
		||||
        self._clear_installed_plugin_info()
 | 
			
		||||
| 
						 | 
				
			
			@ -116,7 +112,8 @@ class PluginsWindow(object):
 | 
			
		|||
        desc_textbuffer = self.plugin_description_textview.get_buffer()
 | 
			
		||||
        desc_textbuffer.set_text(plugin.description)
 | 
			
		||||
        self.plugin_description_textview.set_property('sensitive', True)
 | 
			
		||||
        self.uninstall_plugin_button.set_property('sensitive', True)
 | 
			
		||||
        self.uninstall_plugin_button.set_property('sensitive',
 | 
			
		||||
                                    gajim.PLUGINS_DIRS[1] in plugin.__path__)
 | 
			
		||||
        if plugin.config_dialog is None:
 | 
			
		||||
            self.configure_plugin_button.set_property('sensitive', False)
 | 
			
		||||
        else:
 | 
			
		||||
| 
						 | 
				
			
			@ -143,9 +140,8 @@ class PluginsWindow(object):
 | 
			
		|||
        self.installed_plugins_model.set_sort_column_id(1, gtk.SORT_ASCENDING)
 | 
			
		||||
 | 
			
		||||
        for plugin in pm.plugins:
 | 
			
		||||
            self.installed_plugins_model.append([plugin,
 | 
			
		||||
                                                                                     plugin.name,
 | 
			
		||||
                                                                                     plugin.active])
 | 
			
		||||
            self.installed_plugins_model.append([plugin, plugin.name,
 | 
			
		||||
                plugin.active])
 | 
			
		||||
 | 
			
		||||
    @log_calls('PluginsWindow')
 | 
			
		||||
    def installed_plugins_toggled_cb(self, cell, path):
 | 
			
		||||
| 
						 | 
				
			
			@ -189,14 +185,66 @@ class PluginsWindow(object):
 | 
			
		|||
 | 
			
		||||
    @log_calls('PluginsWindow')
 | 
			
		||||
    def on_uninstall_plugin_button_clicked(self, widget):
 | 
			
		||||
        pass
 | 
			
		||||
        selection = self.installed_plugins_treeview.get_selection()
 | 
			
		||||
        model, iter = selection.get_selected()
 | 
			
		||||
        if iter:
 | 
			
		||||
            plugin = model.get_value(iter, 0)
 | 
			
		||||
            plugin_name = model.get_value(iter, 1).decode('utf-8')
 | 
			
		||||
            is_active = model.get_value(iter, 2)
 | 
			
		||||
            try:
 | 
			
		||||
                gajim.plugin_manager.remove_plugin(plugin)
 | 
			
		||||
            except PluginsystemError, e:
 | 
			
		||||
                dialogs.WarningDialog(_('Unable to properly remove the plugin'),
 | 
			
		||||
                    str(e))
 | 
			
		||||
                return
 | 
			
		||||
            model.remove(iter)
 | 
			
		||||
 | 
			
		||||
    @log_calls('PluginsWindow')
 | 
			
		||||
    def on_install_plugin_button_clicked(self, widget):
 | 
			
		||||
        def _on_plugin_exists(zip_filename):
 | 
			
		||||
            def on_yes(is_checked):
 | 
			
		||||
                plugin = gajim.plugin_manager.install_from_zip(zip_filename,
 | 
			
		||||
                    True)
 | 
			
		||||
                model = self.installed_plugins_model
 | 
			
		||||
 | 
			
		||||
                for row in xrange(len(model)):
 | 
			
		||||
                    if plugin == model[row][0]:
 | 
			
		||||
                        model.remove(model.get_iter((row, 0)))
 | 
			
		||||
                        break
 | 
			
		||||
 | 
			
		||||
                iter_ = model.append([plugin, plugin.name, False])
 | 
			
		||||
                sel = self.installed_plugins_treeview.get_selection()
 | 
			
		||||
                sel.select_iter(iter_)
 | 
			
		||||
 | 
			
		||||
            dialogs.YesNoDialog(_('Plugin already exists'),
 | 
			
		||||
                sectext=_('Overwrite?'), on_response_yes=on_yes)
 | 
			
		||||
 | 
			
		||||
        def _try_install(zip_filename):
 | 
			
		||||
            try:
 | 
			
		||||
                plugin = gajim.plugin_manager.install_from_zip(zip_filename)
 | 
			
		||||
            except PluginsystemError, er_type:
 | 
			
		||||
                error_text = str(er_type)
 | 
			
		||||
                if error_text == _('Plugin already exists'):
 | 
			
		||||
                    _on_plugin_exists(zip_filename)
 | 
			
		||||
                    return
 | 
			
		||||
 | 
			
		||||
                dialogs.WarningDialog(error_text, '"%s"' % zip_filename)
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
            model = self.installed_plugins_model
 | 
			
		||||
            iter_ = model.append([plugin, plugin.name, False])
 | 
			
		||||
            sel = self.installed_plugins_treeview.get_selection()
 | 
			
		||||
            sel.select_iter(iter_)
 | 
			
		||||
 | 
			
		||||
        self.dialog = dialogs.ArchiveChooserDialog(on_response_ok=_try_install)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GajimPluginConfigDialog(gtk.Dialog):
 | 
			
		||||
 | 
			
		||||
    @log_calls('GajimPluginConfigDialog')
 | 
			
		||||
    def __init__(self, plugin, **kwargs):
 | 
			
		||||
        gtk.Dialog.__init__(self, '%s %s'%(plugin.name, _('Configuration')), **kwargs)
 | 
			
		||||
        gtk.Dialog.__init__(self, '%s %s'%(plugin.name, _('Configuration')),
 | 
			
		||||
                                                                    **kwargs)
 | 
			
		||||
        self.plugin = plugin
 | 
			
		||||
        self.add_button('gtk-close', gtk.RESPONSE_CLOSE)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -29,9 +29,12 @@ __all__ = ['PluginManager']
 | 
			
		|||
import os
 | 
			
		||||
import sys
 | 
			
		||||
import fnmatch
 | 
			
		||||
import zipfile
 | 
			
		||||
from shutil import rmtree
 | 
			
		||||
 | 
			
		||||
from common import gajim
 | 
			
		||||
from common import nec
 | 
			
		||||
from common.exceptions import PluginsystemError
 | 
			
		||||
 | 
			
		||||
from plugins.helpers import log, log_calls, Singleton
 | 
			
		||||
from plugins.plugin import GajimPlugin
 | 
			
		||||
| 
						 | 
				
			
			@ -370,7 +373,7 @@ class PluginManager(object):
 | 
			
		|||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    @log_calls('PluginManager')
 | 
			
		||||
    def scan_dir_for_plugins(path):
 | 
			
		||||
    def scan_dir_for_plugins(path, scan_dirs=True):
 | 
			
		||||
        '''
 | 
			
		||||
        Scans given directory for plugin classes.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -414,7 +417,7 @@ class PluginManager(object):
 | 
			
		|||
                        pass
 | 
			
		||||
                        #log.debug('Module not imported successfully. ImportError: %s'%(import_error))
 | 
			
		||||
 | 
			
		||||
                elif os.path.isdir(file_path):
 | 
			
		||||
                elif os.path.isdir(file_path) and scan_dirs:
 | 
			
		||||
                    module_name = elem_name
 | 
			
		||||
                    file_path += os.path.sep
 | 
			
		||||
                    #log.debug('Possible package detected.')
 | 
			
		||||
| 
						 | 
				
			
			@ -454,3 +457,70 @@ class PluginManager(object):
 | 
			
		|||
                    #log.debug(module)
 | 
			
		||||
 | 
			
		||||
        return plugins_found
 | 
			
		||||
 | 
			
		||||
    def install_from_zip(self, zip_filename, owerwrite=None):
 | 
			
		||||
        '''
 | 
			
		||||
        Install plagin from zip and return plugin
 | 
			
		||||
        '''
 | 
			
		||||
        try:
 | 
			
		||||
            zip_file = zipfile.ZipFile(zip_filename)
 | 
			
		||||
        except zipfile.BadZipfile, e:
 | 
			
		||||
            # it is not zip file
 | 
			
		||||
            raise PluginsystemError(_('Archive corrupted'))
 | 
			
		||||
        except IOError,e:
 | 
			
		||||
            raise PluginsystemError(_('Archive empty'))
 | 
			
		||||
 | 
			
		||||
        if zip_file.testzip():
 | 
			
		||||
            # CRC error
 | 
			
		||||
            raise PluginsystemError(_('Archive corrupted'))
 | 
			
		||||
 | 
			
		||||
        dirs = []
 | 
			
		||||
        for filename in zip_file.namelist():
 | 
			
		||||
            if filename.startswith('.') or filename.startswith('/') or \
 | 
			
		||||
            ('/' not in filename):
 | 
			
		||||
                # members not safe
 | 
			
		||||
                raise PluginsystemError(_('Archive is malformed'))
 | 
			
		||||
            if filename.endswith('/') and filename.find('/', 0, -1) < 0:
 | 
			
		||||
                dirs.append(filename)
 | 
			
		||||
 | 
			
		||||
        if len(dirs) > 1:
 | 
			
		||||
            # several directories in the root of the archive
 | 
			
		||||
            raise PluginsystemError(_('Archive is malformed'))
 | 
			
		||||
 | 
			
		||||
        base_dir, user_dir = gajim.PLUGINS_DIRS
 | 
			
		||||
        plugin_dir = os.path.join(user_dir, dirs[0])
 | 
			
		||||
 | 
			
		||||
        if os.path.isdir(plugin_dir):
 | 
			
		||||
        # Plugin already exists
 | 
			
		||||
            if not owerwrite:
 | 
			
		||||
                raise PluginsystemError(_('Plugin already exists'))
 | 
			
		||||
            self.remove_plugin(self.get_plugin_by_path(plugin_dir))
 | 
			
		||||
 | 
			
		||||
        zip_file.extractall(user_dir)
 | 
			
		||||
        zip_file.close()
 | 
			
		||||
        path = os.path.join(user_dir, dirs[0])
 | 
			
		||||
        self.add_plugin(self.scan_dir_for_plugins(plugin_dir, False)[0])
 | 
			
		||||
        plugin = self.plugins[-1]
 | 
			
		||||
        return plugin
 | 
			
		||||
 | 
			
		||||
    def remove_plugin(self, plugin):
 | 
			
		||||
        '''
 | 
			
		||||
        Deactivate and remove plugin from `plugins` list
 | 
			
		||||
        '''
 | 
			
		||||
        def on_error(func, path, error):
 | 
			
		||||
            if func == os.path.islink:
 | 
			
		||||
            # if symlink
 | 
			
		||||
                os.unlink(path)
 | 
			
		||||
                return
 | 
			
		||||
            # access is denied or other
 | 
			
		||||
            raise PluginsystemError(error[1])
 | 
			
		||||
 | 
			
		||||
        if plugin.active:
 | 
			
		||||
            self.deactivate_plugin(plugin)
 | 
			
		||||
        rmtree(plugin.__path__, False, on_error)
 | 
			
		||||
        self.plugins.remove(plugin)
 | 
			
		||||
 | 
			
		||||
    def get_plugin_by_path(self, plugin_dir):
 | 
			
		||||
        for plugin in self.plugins:
 | 
			
		||||
            if plugin.__path__ in plugin_dir:
 | 
			
		||||
                return plugin
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue