[Sugar-devel] [sugar PATCH] "Proxy" feature. Wiki page: http://wiki.sugarlabs.org/go/Features/Proxy_Settings
Simon Schampijer
simon at schampijer.de
Tue Feb 19 05:30:42 EST 2013
Hi Ajay,
there are some questions open from the last time this feature was
submitted for review:
http://lists.sugarlabs.org/archive/sugar-devel/2012-August/038917.html
"There are two differences to the GNOME 3 design. There is a check box
in the 'Manual' option that says 'Use authentication'. And there is an
option 'Ignored Hosts'. Can you give a bit of background on those, why
you did add those, what issue we have on the XO or discovered does it
solve?"
On 02/17/2013 11:28 AM, Ajay Garg wrote:
> This patch is to be applied on the master-branch.
>
> Some details of this patch ::
>
> a)
> The proxy-settings are stored both in the "gconf" schema; and "dconf"
> schema.
Can you elaborate why you have chosen to also store into dconf? At one
point Sugar wants to switch to Gsettings. Would that help for that case?
Regards,
Simon
> b)
> The package "gnome-vfs2" is a pre-requisite for this patch to work,
> since the settings are (also) stored in the dconf schema.
>
> c)
> Also, a slightly unrelated change is the persistence of the values in
> the "Network" control-panel.
>
> Earlier, there was a timeout involved (of 3000 milliseconds), which
> would trigger the saving of the values (for eg., "Collaboration Server"
> field was persisted this way).
>
> Now, the values are persisted SYNCHRONOUSLY, when the "tick"
> toolbar-button is clicked (of course, all the changes are undone, if the
> user instead decides to "Cancel changes").
>
> d)
> All the use-cases of the proxy, are working, as per the wiki page
> http://wiki.sugarlabs.org/go/Features/Proxy_Settings
>
> Signed-off-by: Ajay Garg <ajay at activitycentral.com>
> ---
> extensions/cpsection/network/view.py | 695 +++++++++++++++++++++++++++++++--
> src/jarabe/controlpanel/gui.py | 2 +
> src/jarabe/controlpanel/sectionview.py | 8 +
> src/jarabe/main.py | 36 ++
> 4 files changed, 707 insertions(+), 34 deletions(-)
>
> diff --git a/extensions/cpsection/network/view.py b/extensions/cpsection/network/view.py
> index b360759..99b792b 100644
> --- a/extensions/cpsection/network/view.py
> +++ b/extensions/cpsection/network/view.py
> @@ -16,10 +16,19 @@
>
> from gi.repository import Gtk
> from gi.repository import Gdk
> +from gi.repository import GConf
> from gi.repository import GObject
> +from gi.repository import Gio
> +from gi.repository import Pango
> from gettext import gettext as _
>
> +import os
> +import subprocess
> +import logging
> +
> from sugar3.graphics import style
> +from sugar3.graphics.alert import Alert
> +from sugar3.graphics.icon import Icon
>
> from jarabe.controlpanel.sectionview import SectionView
> from jarabe.controlpanel.inlinealert import InlineAlert
> @@ -31,6 +40,471 @@ TITLE = _('Network')
>
> _APPLY_TIMEOUT = 3000
>
> +# Please refer ::
> +# http://developer.gnome.org/ProxyConfiguration/
> +
> +GSETTINGS_PROXY = Gio.Settings.new('org.gnome.system.proxy')
> +GSETTINGS_PROXY_FTP = Gio.Settings.new('org.gnome.system.proxy.ftp')
> +GSETTINGS_PROXY_HTTP = Gio.Settings.new('org.gnome.system.proxy.http')
> +GSETTINGS_PROXY_HTTPS = Gio.Settings.new('org.gnome.system.proxy.https')
> +GSETTINGS_PROXY_SOCKS = Gio.Settings.new('org.gnome.system.proxy.socks')
> +
> +
> +client = GConf.Client.get_default()
> +
> +
> +class GConfMixin(object):
> + """Mix-in class for GTK widgets backed by GConf"""
> + def __init__(self, gconf_key, gsettings_dconf, dconf_key, widget=None, signal='changed'):
> + self._gconf_key = gconf_key
> + self._gsettings_dconf = gsettings_dconf
> + self._dconf_key = dconf_key
> + self._notify_id = client.notify_add(gconf_key, self.__gconf_notify_cb, None)
> + initial_value = self._get_gconf_value()
> + self._undo_value = initial_value
> + self.set_value_from_gconf(initial_value)
> + widget = widget or self
> +
> + def undo(self):
> + """Revert to original value if modified"""
> + if not self.changed:
> + return
> + logging.debug('Reverting %r to %r', self._gconf_key, self._undo_value)
> + self._set_gconf_value(self._undo_value)
> +
> + def get_value_for_gconf(self):
> + """
> + Return the current value of the widget in a format suitable for GConf
> +
> + MUST be implemented by subclasses.
> + """
> + raise NotImplementedError()
> +
> + def set_value_from_gconf(self, value):
> + """
> + Set the current value of the widget based on a value from GConf
> + MUST be implemented by subclasses.
> + """
> + raise NotImplementedError()
> +
> + def __gconf_notify_cb(self, client, transaction_id_, entry, user_data_):
> + new_value = _gconf_value_to_python(entry.value)
> + self.set_value_from_gconf(new_value)
> +
> + def _commit(self, widget):
> + new_value = self.get_value_for_gconf()
> + logging.debug('Setting %r to %r', self._gconf_key, new_value)
> +
> + self._set_gconf_value(new_value)
> +
> + def _set_gconf_value(self, new_value):
> + gconf_type = client.get(self._gconf_key).type
> + if gconf_type == GConf.ValueType.STRING:
> + client.set_string(self._gconf_key, new_value)
> + self._gsettings_dconf.set_string(self._dconf_key, new_value)
> + elif gconf_type == GConf.ValueType.INT:
> + client.set_int(self._gconf_key, new_value)
> + self._gsettings_dconf.set_int(self._dconf_key, new_value)
> + elif gconf_type == GConf.ValueType.FLOAT:
> + client.set_float(self._gconf_key, new_value)
> + self._gsettings_dconf.set_double(self._dconf_key, new_value)
> + elif gconf_type == GConf.ValueType.BOOL:
> + client.set_bool(self._gconf_key, new_value)
> + self._gsettings_dconf.set_boolean(self._dconf_key, new_value)
> + elif gconf_type == GConf.ValueType.LIST:
> + import traceback
> + list_type = client.get(self._gconf_key).get_list_type()
> +
> + # Persisting the value of a "LIST" via shell, unless and
> + # until http://bugs.sugarlabs.org/ticket/3926 gets solved.
> + commit_list = []
> + for value in new_value:
> + translated_value = value.translate(None, "' ")
> + commit_list.append(translated_value)
> +
> + environment = os.environ.copy()
> + try:
> + process = subprocess.Popen(['gconftool-2 '
> + '--type list '
> + '--list-type string '
> + '--set %s \'%s\'' % (self._gconf_key,
> + commit_list)],
> + stdout=subprocess.PIPE,
> + env=environment,
> + shell=True)
> + process.wait()
> +
> + self._gsettings_dconf.set_strv(self._dconf_key, new_value)
> + except Exception, e:
> + logging.exception(e)
> + #client.set_list(self._gconf_key, list_type, new_value)
> + else:
> + raise TypeError('Cannot store %r in GConf' % (new_value, ))
> +
> + def _get_gconf_value(self):
> + return _gconf_value_to_python(client.get(self._gconf_key))
> +
> + def changed(self):
> + return self._undo_value != self.get_value_for_gconf()
> +
> +
> +class GConfEntry(Gtk.Entry, GConfMixin):
> + """Text entry backed by GConf
> +
> + It is the callers responsibility to call GConfClient.add_dir() for the
> + GConf directory containing the key.
> + """
> +
> + def __init__(self, gconf_key, gsettings_dconf, dconf_key):
> + Gtk.Entry.__init__(self)
> + GConfMixin.__init__(self, gconf_key, gsettings_dconf, dconf_key)
> +
> + def get_value_for_gconf(self):
> + return self.props.text
> +
> + def set_value_from_gconf(self, value):
> + self.props.text = value
> +
> +
> +class GConfIntegerSpinButton(Gtk.SpinButton, GConfMixin):
> + """Integer SpinButton backed by GConf
> + It is the callers responsibility to call GConfClient.add_dir() for the
> + GConf directory containing the key.
> + """
> +
> + def __init__(self, gconf_key, gsettings_dconf, dconf_key, adjustment, climb_rate=0):
> + Gtk.SpinButton.__init__(self, adjustment=adjustment, climb_rate=climb_rate)
> + GConfMixin.__init__(self, gconf_key, gsettings_dconf, dconf_key)
> +
> + def get_value_for_gconf(self):
> + return self.get_value_as_int()
> +
> + def set_value_from_gconf(self, value):
> + self.set_value(value)
> +
> +
> +class GConfStringListEntry(GConfEntry):
> + """Text entry backed by a GConf list of strings"""
> +
> + def __init__(self, gconf_key, gsettings_dconf, dconf_key, separator=','):
> + self._separator = separator
> + GConfEntry.__init__(self, gconf_key, gsettings_dconf, dconf_key)
> +
> + def get_value_for_gconf(self):
> + entries = self.props.text.split(self._separator)
> + return [entry for entry in entries if entry]
> +
> + def set_value_from_gconf(self, value):
> + self.props.text = self._separator.join(value)
> +
> +
> +class SettingBox(Gtk.HBox):
> + """
> + Base class for "lines" on the screen representing configuration settings
> + """
> +
> + def __init__(self, name, size_group=None):
> + Gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING)
> + self.label = Gtk.Label(name)
> + self.label.modify_fg(Gtk.StateType.NORMAL,
> + style.COLOR_SELECTION_GREY.get_gdk_color())
> + self.label.set_alignment(1, 0.5)
> + self.label.show()
> + self.pack_start(self.label, False, False, 0)
> +
> + if size_group is not None:
> + size_group.add_widget(self.label)
> +
> +
> +class GConfStringSettingBox(SettingBox):
> + """A configuration line for a GConf string setting"""
> +
> + def __init__(self, name, gconf_key, gsettings_dconf, dconf_key, size_group=None):
> + SettingBox.__init__(self, name, size_group=size_group)
> + self.string_entry = GConfEntry(gconf_key, gsettings_dconf, dconf_key)
> + self.string_entry.show()
> + self.pack_start(self.string_entry, True, True, 0)
> +
> + def undo(self):
> + """Revert to original value if modified"""
> + self.string_entry.undo()
> +
> + def _commit(self, widget):
> + self.string_entry._commit(self.string_entry)
> +
> + @property
> + def changed(self):
> + return self.string_entry.changed
> +
> +
> +class GConfPasswordSettingBox(GConfStringSettingBox):
> + """A configuration line for a GConf password setting"""
> +
> + def __init__(self, name, gconf_key, gsettings_dconf, dconf_key, size_group=None):
> + GConfStringSettingBox.__init__(self, name, gconf_key,
> + gsettings_dconf, dconf_key, size_group)
> + self.string_entry.set_visibility(False)
> +
> +
> +class GConfHostListSettingBox(GConfStringSettingBox):
> + """A configuration line for a host list GConf setting"""
> +
> + def __init__(self, name, gconf_key, gsettings_dconf, dconf_key, size_group=None):
> + SettingBox.__init__(self, name, size_group=size_group)
> + self.hosts_entry = GConfStringListEntry(gconf_key,
> + gsettings_dconf, dconf_key)
> + self.hosts_entry.show()
> + self.pack_start(self.hosts_entry, True, True, 0)
> +
> + def undo(self):
> + """Revert to original value if modified"""
> + self.hosts_entry.undo()
> +
> + def _commit(self, widget):
> + self.hosts_entry._commit(self.hosts_entry)
> +
> + @property
> + def changed(self):
> + return self.hosts_entry.changed
> +
> +class GConfHostPortSettingBox(SettingBox):
> + """A configuration line for a combined host name and port GConf setting"""
> +
> + def __init__(self, name, host_key, port_key, gsettings_dconf,
> + dconf_host_key, dconf_port_key, size_group=None):
> + SettingBox.__init__(self, name, size_group=size_group)
> + self.host_name_entry = GConfEntry(host_key, gsettings_dconf,
> + dconf_host_key)
> + self.host_name_entry.show()
> + self.pack_start(self.host_name_entry, True, True, 0)
> +
> + # port number 0 means n/a
> + adjustment = Gtk.Adjustment(0, 0, 65535, 1, 10)
> + self.port_spin_button = GConfIntegerSpinButton(port_key,
> + gsettings_dconf,
> + dconf_port_key,
> + adjustment,
> + climb_rate=0.1)
> + self.port_spin_button.show()
> + self.pack_start(self.port_spin_button, False, False, 0)
> +
> + def undo(self):
> + """Revert to original values if modified"""
> + self.host_name_entry.undo()
> + self.port_spin_button.undo()
> +
> + def _commit(self, widget):
> + self.host_name_entry._commit(self.host_name_entry)
> + self.port_spin_button._commit(self.port_spin_button)
> +
> + @property
> + def changed(self):
> + return self.host_name_entry.changed or self.port_spin_button.changed
> +
> +
> +class ExclusiveOptionSetsBox(Gtk.VBox):
> + """
> + Container for sets of different settings selected by a top-level setting
> + Renders the top level setting as a ComboBox. Only the currently
> + active set is shown on screen.
> + """
> + def __init__(self, top_name, option_sets, size_group=None):
> + """Initialize an ExclusiveOptionSetsBox instance
> +
> + Arguments:
> +
> + top_name -- text label used for the top-level selection
> + option_sets -- list of tuples containing text label and GTK
> + widget to display for each of the option sets
> + size_group -- optional gtk.SizeGroup to use for the top-level label
> + """
> + Gtk.VBox.__init__(self, spacing=style.DEFAULT_SPACING)
> + self.label_size_group = size_group
> + top_box = Gtk.HBox(spacing=style.DEFAULT_SPACING)
> + top_box.show()
> + top_label = Gtk.Label(top_name)
> + top_label.modify_fg(Gtk.StateType.NORMAL,
> + style.COLOR_SELECTION_GREY.get_gdk_color())
> + top_label.set_alignment(1, 0.5)
> + top_label.show()
> + self.label_size_group.add_widget(top_label)
> + top_box.pack_start(top_label, False, False, 0)
> +
> + model = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_OBJECT)
> + self._top_combo_box = Gtk.ComboBox(model=model)
> + self._top_combo_box.connect('changed', self.__combo_changed_cb)
> + self._top_combo_box.show()
> +
> + cell_renderer = Gtk.CellRendererText()
> + cell_renderer.props.ellipsize = Pango.EllipsizeMode.MIDDLE
> + cell_renderer.props.ellipsize_set = True
> + self._top_combo_box.pack_start(cell_renderer, True)
> + self._top_combo_box.add_attribute(cell_renderer, 'text', 0)
> + top_box.pack_start(self._top_combo_box, True, True, 0)
> + self.pack_start(top_box, False, False, 0)
> +
> + self._settings_box = Gtk.VBox()
> + self._settings_box.show()
> + self.pack_start(self._settings_box, False, False, 0)
> +
> + for name, box in option_sets:
> + model.append((name, box))
> +
> + def __combo_changed_cb(self, combobox):
> + giter = combobox.get_active_iter()
> + new_box = combobox.get_model().get(giter, 1)[0]
> + current_box = self._settings_box.get_children()
> + if current_box:
> + self._settings_box.remove(current_box[0])
> +
> + self._settings_box.add(new_box)
> + new_box.show()
> +
> +
> +class GConfExclusiveOptionSetsBox(ExclusiveOptionSetsBox, GConfMixin):
> + """
> + Container for sets of GConf settings based on a top-level setting
> + """
> +
> + def __init__(self, top_name, top_gconf_key, gsettings_dconf,
> + dconf_key, option_sets, size_group=None):
> + """Initialize a GConfExclusiveOptionSetsBox instance
> +
> + Arguments:
> +
> + top_name -- text label used for the top-level selection
> + top_gconf_key -- key for the GConf entry to use for the
> + top-level selection
> + option_sets -- list of tuples containing text label, matching
> + GConf value as well as the GTK widget to display
> + for each of the option sets
> + size_group -- optional gtk.SizeGroup to use for the top-level label
> + """
> + display_sets = [(name, widget) for name, value, widget in option_sets]
> + self._top_mapping = dict([(name, value)
> + for name, value, widget in option_sets])
> + ExclusiveOptionSetsBox.__init__(self, top_name, display_sets,
> + size_group=size_group)
> + GConfMixin.__init__(self, top_gconf_key, gsettings_dconf,
> + dconf_key, self._top_combo_box)
> +
> + def get_value_for_gconf(self):
> + giter = self._top_combo_box.get_active_iter()
> + if giter is None:
> + return None
> + name = self._top_combo_box.get_model().get(giter, 0)[0]
> + return self._top_mapping[name]
> +
> + def set_value_from_gconf(self, value):
> + for idx, (name, widget_) in enumerate(self._top_combo_box.get_model()):
> + if self._top_mapping[name] == value:
> + self._top_combo_box.set_active(idx)
> + return
> +
> + raise ValueError('Invalid value %r' % (value, ))
> +
> +
> +class SpecialGConfExclusiveOptionSetsBox(GConfExclusiveOptionSetsBox):
> + def __init__(self, top_name, top_gconf_key, gsettings_dconf,
> + dconf_key, option_sets, size_group=None):
> + GConfExclusiveOptionSetsBox.__init__(self, top_name, top_gconf_key,
> + gsettings_dconf, dconf_key,
> + option_sets, size_group)
> + self._initial_enabled_value = True
> + if self._undo_value == 'none':
> + self._initial_enabled_value = False
> +
> + def undo(self):
> + """Revert to original value if modified"""
> + if not self.changed:
> + return
> + logging.debug('Reverting %r to %r', self._gconf_key, self._undo_value)
> + self._set_gconf_value(self._undo_value)
> +
> + # Also, set the initial value for "org.gnome.system.proxy.http enabled"
> + logging.debug('Reverting org.gnome.system.proxy.http enabled to %r', self._initial_enabled_value)
> + GSETTINGS_PROXY_HTTP.set_boolean('enabled', self._initial_enabled_value)
> +
> + def _commit(self, widget):
> + """Commit the base key."""
> + super(SpecialGConfExclusiveOptionSetsBox, self)._commit(None)
> +
> + """Plus commit the dconf key :: org.gnome.system.proxy.http enabled"""
> + # The logic to set the value is ::
> + #
> + # * If the mode is "none", the key is to be set to false.
> + #
> + # * If the mode is not "none" (in other words, it is "manual"
> + # or "automatic"), the key is to be set to true.
> + enabled_value = True
> + mode = self.get_value_for_gconf()
> + if mode == 'none':
> + enabled_value = False
> +
> + logging.debug('Setting org.gnome.system.proxy.http enabled to %r', enabled_value)
> + GSETTINGS_PROXY_HTTP.set_boolean('enabled', enabled_value)
> +
> + def changed(self):
> + return self._undo_value != self.get_value_for_gconf()
> +
> +
> +class OptionalSettingsBox(Gtk.VBox):
> + """
> + Container for settings (de)activated by a top-level setting
> +
> + Renders the top level setting as a CheckButton. The settings are only
> + shown on screen if the top-level setting is enabled.
> + """
> + def __init__(self, top_name, options):
> + """Initialize an OptionalSettingsBox instance
> + Arguments:
> +
> + top_name -- text label used for the top-level selection
> + options -- list of GTK widgets to display for each of the options
> + """
> + Gtk.VBox.__init__(self, spacing=style.DEFAULT_SPACING)
> + self._top_check_button = Gtk.CheckButton()
> + self._top_check_button.props.label = top_name
> + self._top_check_button.connect('toggled', self.__button_changed_cb)
> + self._top_check_button.show()
> + self.pack_start(self._top_check_button, True, True, 0)
> + self._settings_box = Gtk.VBox(spacing=style.DEFAULT_SPACING)
> + for box in options:
> + self._settings_box.pack_start(box, True, True, 0)
> +
> + def __button_changed_cb(self, check_button):
> + if check_button.get_active():
> + self._settings_box.show()
> + else:
> + self._settings_box.hide()
> +
> +
> +class GConfOptionalSettingsBox(OptionalSettingsBox, GConfMixin):
> + """
> + Container for GConf settings (de)activated by a top-level setting
> + """
> + def __init__(self, top_name, top_gconf_key, gsettings_dconf,
> + dconf_key, options):
> + """Initialize a GConfExclusiveOptionSetsBox instance
> + Arguments:
> +
> + top_name -- text label used for the top-level selection
> + top_gconf_key -- key for the GConf entry to use for the
> + top-level selection
> + options -- list of GTK widgets to display for each of the options
> + """
> + OptionalSettingsBox.__init__(self, top_name, options)
> + GConfMixin.__init__(self, top_gconf_key, gsettings_dconf,
> + dconf_key, self._top_check_button,
> + signal='toggled')
> +
> + def get_value_for_gconf(self):
> + return self._top_check_button.get_active()
> +
> + def set_value_from_gconf(self, value):
> + self._top_check_button.set_active(value)
> + self.pack_start(self._settings_box, False, False, 0)
> +
>
> class Network(SectionView):
> def __init__(self, model, alerts):
> @@ -44,6 +518,10 @@ class Network(SectionView):
> self._jabber_change_handler = None
> self._radio_change_handler = None
> self._network_configuration_reset_handler = None
> + self._undo_objects = []
> +
> + client.add_dir('/system/http_proxy', GConf.ClientPreloadType.PRELOAD_ONELEVEL)
> + client.add_dir('/system/proxy', GConf.ClientPreloadType.PRELOAD_ONELEVEL)
>
> self.set_border_width(style.DEFAULT_SPACING * 2)
> self.set_spacing(style.DEFAULT_SPACING)
> @@ -174,10 +652,141 @@ class Network(SectionView):
> workspace.pack_start(box_mesh, False, True, 0)
> box_mesh.show()
>
> + proxy_separator = Gtk.HSeparator()
> + workspace.pack_start(proxy_separator, False, False, 0)
> + proxy_separator.show()
> +
> + self._add_proxy_section(workspace)
> +
> self.setup()
>
> + def _add_proxy_section(self, workspace):
> + proxy_title = Gtk.Label(_('Proxy'))
> + proxy_title.set_alignment(0, 0)
> + proxy_title.show()
> + workspace.pack_start(proxy_title, False, False, 0)
> +
> + proxy_box = Gtk.VBox()
> + proxy_box.set_border_width(style.DEFAULT_SPACING * 2)
> + proxy_box.set_spacing(style.DEFAULT_SPACING)
> + proxy_box.show()
> +
> + workspace.pack_start(proxy_box, True, True, 0)
> +
> + size_group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
> +
> + automatic_proxy_box = Gtk.VBox()
> + automatic_proxy_box.set_spacing(style.DEFAULT_SPACING)
> +
> + url_box = GConfStringSettingBox(_('Configuration URL:'),
> + '/system/proxy/autoconfig_url',
> + GSETTINGS_PROXY,
> + 'autoconfig-url',
> + size_group)
> + url_box.show()
> + automatic_proxy_box.pack_start(url_box, True, True, 0)
> + self._undo_objects.append(url_box)
> +
> + wpad_help_text = _('Web Proxy Autodiscovery (WPAD) is used when a'
> + ' Configuration URL is not provided. This is not'
> + ' recommended for untrusted public networks.')
> + automatic_proxy_help = Gtk.Label(wpad_help_text)
> + automatic_proxy_help.set_alignment(0, 0)
> + automatic_proxy_help.set_line_wrap(True)
> + automatic_proxy_help.show()
> + automatic_proxy_box.pack_start(automatic_proxy_help, True, True, 0)
> +
> + manual_proxy_box = Gtk.VBox()
> + manual_proxy_box.set_spacing(style.DEFAULT_SPACING)
> +
> + http_box = GConfHostPortSettingBox(_('HTTP Proxy:'),
> + '/system/http_proxy/host',
> + '/system/http_proxy/port',
> + GSETTINGS_PROXY_HTTP,
> + 'host',
> + 'port',
> + size_group)
> + http_box.show()
> + manual_proxy_box.pack_start(http_box, True, True, 0)
> + self._undo_objects.append(http_box)
> +
> + user_name_box = GConfStringSettingBox(_('Username:'),
> + '/system/http_proxy/authentication_user',
> + GSETTINGS_PROXY_HTTP, 'authentication-user', size_group)
> + user_name_box.show()
> + self._undo_objects.append(user_name_box)
> +
> + password_box = GConfPasswordSettingBox(_('Password:'),
> + '/system/http_proxy/authentication_password',
> + GSETTINGS_PROXY_HTTP, 'authentication-password', size_group)
> + password_box.show()
> + self._undo_objects.append(password_box)
> +
> + auth_box = GConfOptionalSettingsBox(_('Use authentication'),
> + '/system/http_proxy/use_authentication',
> + GSETTINGS_PROXY_HTTP,
> + 'use-authentication',
> + [user_name_box, password_box])
> + auth_box.show()
> + manual_proxy_box.pack_start(auth_box, True, True, 0)
> + self._undo_objects.append(auth_box)
> +
> + https_box = GConfHostPortSettingBox(_('HTTPS Proxy:'),
> + '/system/proxy/secure_host',
> + '/system/proxy/secure_port',
> + GSETTINGS_PROXY_HTTPS,
> + 'host',
> + 'port',
> + size_group)
> + https_box.show()
> + manual_proxy_box.pack_start(https_box, True, True, 0)
> + self._undo_objects.append(https_box)
> +
> + ftp_box = GConfHostPortSettingBox(_('FTP Proxy:'),
> + '/system/proxy/ftp_host',
> + '/system/proxy/ftp_port',
> + GSETTINGS_PROXY_FTP,
> + 'host',
> + 'port',
> + size_group)
> + ftp_box.show()
> + manual_proxy_box.pack_start(ftp_box, True, True, 0)
> + self._undo_objects.append(ftp_box)
> +
> + socks_box = GConfHostPortSettingBox(_('SOCKS Proxy:'),
> + '/system/proxy/socks_host',
> + '/system/proxy/socks_port',
> + GSETTINGS_PROXY_SOCKS,
> + 'host',
> + 'port',
> + size_group)
> + socks_box.show()
> + manual_proxy_box.pack_start(socks_box, True, True, 0)
> + self._undo_objects.append(socks_box)
> +
> + option_sets = [('None', 'none', Gtk.VBox()),
> + ('Automatic', 'auto', automatic_proxy_box),
> + ('Manual', 'manual', manual_proxy_box)]
> + option_sets_box = SpecialGConfExclusiveOptionSetsBox(_('Method:'),
> + '/system/proxy/mode',
> + GSETTINGS_PROXY,
> + 'mode',
> + option_sets, size_group)
> + option_sets_box.show()
> + proxy_box.pack_start(option_sets_box, False, False, 0)
> + self._undo_objects.append(option_sets_box)
> +
> + no_proxy_box = GConfHostListSettingBox(_('Ignored Hosts'),
> + '/system/http_proxy/ignore_hosts', GSETTINGS_PROXY,
> + 'ignore-hosts', size_group)
> + no_proxy_box.show()
> + proxy_box.pack_start(no_proxy_box, False, False, 0)
> + self._undo_objects.append(no_proxy_box)
> +
> +
> def setup(self):
> - self._entry.set_text(self._model.get_jabber())
> + self._old_jabber_entry = self._model.get_jabber()
> + self._entry.set_text(self._old_jabber_entry)
> try:
> radio_state = self._model.get_radio()
> except self._model.ReadError, detail:
> @@ -191,24 +800,42 @@ class Network(SectionView):
> self.needs_restart = False
> self._radio_change_handler = self._button.connect( \
> 'toggled', self.__radio_toggled_cb)
> - self._jabber_change_handler = self._entry.connect( \
> - 'changed', self.__jabber_changed_cb)
> self._network_configuration_reset_handler = \
> self._clear_history_button.connect( \
> 'clicked', self.__network_configuration_reset_cb)
>
> def undo(self):
> self._button.disconnect(self._radio_change_handler)
> - self._entry.disconnect(self._jabber_change_handler)
> self._model.undo()
> self._jabber_alert.hide()
> + self._entry.set_text(self._old_jabber_entry)
> self._radio_alert.hide()
> + for setting in self._undo_objects:
> + setting.undo()
> +
> + # pylint: disable=E0202
> + @property
> + def needs_restart(self):
> + # Some parts of Sugar as well as many non-Gnome applications
> + # use environment variables rather than gconf for proxy
> + # settings, so we need to restart for the changes to take
> + # _full_ effect.
> + if self._entry.get_text() != self._old_jabber_entry:
> + return True
> +
> + for setting in self._undo_objects:
> + if setting.changed():
> + return True
>
> - def _validate(self):
> - if self._jabber_valid and self._radio_valid:
> - self.props.is_valid = True
> - else:
> - self.props.is_valid = False
> + return False
> +
> + # pylint: disable=E0102,E1101
> + @needs_restart.setter
> + def needs_restart(self, value):
> + # needs_restart is a property (i.e. gets calculated) in this Control
> + # Panel, but SectionView.__init__() wants to initialise it to False,
> + # so we need to provide a (fake) setter.
> + pass
>
> def __radio_toggled_cb(self, widget, data=None):
> radio_state = widget.get_active()
> @@ -225,34 +852,34 @@ class Network(SectionView):
> self._validate()
> return False
>
> - def __jabber_changed_cb(self, widget, data=None):
> - if self._jabber_sid:
> - GObject.source_remove(self._jabber_sid)
> - self._jabber_sid = GObject.timeout_add(_APPLY_TIMEOUT,
> - self.__jabber_timeout_cb,
> - widget)
> -
> - def __jabber_timeout_cb(self, widget):
> - self._jabber_sid = 0
> - if widget.get_text() == self._model.get_jabber:
> - return
> - try:
> - self._model.set_jabber(widget.get_text())
> - except self._model.ReadError, detail:
> - self._jabber_alert.props.msg = detail
> - self._jabber_valid = False
> - self._jabber_alert.show()
> - self.restart_alerts.append('jabber')
> - else:
> - self._jabber_valid = True
> - self._jabber_alert.hide()
> -
> - self._validate()
> - return False
> -
> def __network_configuration_reset_cb(self, widget):
> # FIXME: takes effect immediately, not after CP is closed with
> # confirmation button
> self._model.clear_networks()
> if not self._model.have_networks():
> self._clear_history_button.set_sensitive(False)
> +
> + def perform_accept_actions(self):
> + current_jabber_entry = self._entry.get_text()
> + if current_jabber_entry != self._old_jabber_entry:
> + self._model.set_jabber(current_jabber_entry)
> +
> + for setting in self._undo_objects:
> + setting._commit(setting)
> +
> +
> +def _gconf_value_to_python(gconf_value):
> + if gconf_value.type == GConf.ValueType.STRING:
> + return gconf_value.get_string()
> + elif gconf_value.type == GConf.ValueType.INT:
> + return gconf_value.get_int()
> + elif gconf_value.type == GConf.ValueType.FLOAT:
> + return gconf_value.get_float()
> + elif gconf_value.type == GConf.ValueType.BOOL:
> + return gconf_value.get_bool()
> + elif gconf_value.type == GConf.ValueType.LIST:
> + return [_gconf_value_to_python(entry)
> + for entry in gconf_value.get_list()]
> + else:
> + raise TypeError("Don't know how to handle GConf value"
> + " type %r" % (gconf_value.type, ))
> diff --git a/src/jarabe/controlpanel/gui.py b/src/jarabe/controlpanel/gui.py
> index f28b248..7805bb2 100644
> --- a/src/jarabe/controlpanel/gui.py
> +++ b/src/jarabe/controlpanel/gui.py
> @@ -302,12 +302,14 @@ class ControlPanel(Gtk.Window):
> return options
>
> def __cancel_clicked_cb(self, widget):
> + self._section_view.perform_cancel_actions()
> self._section_view.undo()
> self._options[self._current_option]['alerts'] = []
> self._section_toolbar.accept_button.set_sensitive(True)
> self._show_main_view()
>
> def __accept_clicked_cb(self, widget):
> + self._section_view.perform_accept_actions()
> if self._section_view.needs_restart:
> self._section_toolbar.accept_button.set_sensitive(False)
> self._section_toolbar.cancel_button.set_sensitive(False)
> diff --git a/src/jarabe/controlpanel/sectionview.py b/src/jarabe/controlpanel/sectionview.py
> index cbf4768..bee64e0 100644
> --- a/src/jarabe/controlpanel/sectionview.py
> +++ b/src/jarabe/controlpanel/sectionview.py
> @@ -52,3 +52,11 @@ class SectionView(Gtk.VBox):
> def undo(self):
> """Undo here the changes that have been made in this section."""
> pass
> +
> + def perform_cancel_actions(self):
> + """Perform additional actions, when the "Cancel" button is clicked."""
> + pass
> +
> + def perform_accept_actions(self):
> + """Perform additional actions, when the "Ok" button is clicked."""
> + pass
> diff --git a/src/jarabe/main.py b/src/jarabe/main.py
> index 44880ba..e4893a1 100755
> --- a/src/jarabe/main.py
> +++ b/src/jarabe/main.py
> @@ -230,6 +230,41 @@ def setup_theme():
> icons_path = os.path.join(config.data_path, 'icons')
> Gtk.IconTheme.get_default().append_search_path(icons_path)
>
> +def export_proxy_settings():
> + """
> + Export manual proxy settings from GConf as environment variables
> +
> + Some applications and tools and even some parts of Sugar will use
> + the http_proxy environment variable if set, but don't use the Gnome
> + (GConf) proxy settings.
> + """
> + client = GConf.Client.get_default()
> +
> + # Note: See https://dev.laptop.org.au/issues/1179#note-9
> + if client.get_string('/system/proxy/mode') == 'none':
> + return
> +
> + http_host = client.get_string('/system/http_proxy/host')
> + http_port = client.get_int('/system/http_proxy/port')
> + use_auth = client.get_bool('/system/http_proxy/use_authentication')
> + if use_auth:
> + user = client.get_string('/system/http_proxy/authentication_user')
> + pw = client.get_string('/system/http_proxy/authentication_password')
> + http_proxy = 'http://%s:%s@%s:%d/' % (user, pw, http_host, http_port)
> + else:
> + http_proxy = 'http://%s:%d/' % (http_host, http_port)
> +
> + os.environ['http_proxy'] = http_proxy
> +
> + ignore_hosts = []
> + ignore_hosts_list = client.get('/system/http_proxy/ignore_hosts')
> +
> + # Process, only if the "ignore_hosts_list" is non-empty.
> + if ignore_hosts_list:
> + for entry in ignore_hosts_list.get_list():
> + ignore_hosts.append(entry.get_string())
> + os.environ['no_proxy'] = ','.join(ignore_hosts)
> +
> def _start_intro():
> window = IntroWindow()
> window.connect('done', __intro_window_done_cb)
> @@ -246,6 +281,7 @@ def main():
>
> _start_window_manager()
>
> + export_proxy_settings()
> setup_locale()
> setup_fonts()
> setup_theme()
>
More information about the Sugar-devel
mailing list