Ticket #3070: 0001-Add-proxy-configuration-support-to-Network-Control-P.2.patch

File 0001-Add-proxy-configuration-support-to-Network-Control-P.2.patch, 14.8 KB (added by manuq, 11 years ago)

Adjusted patch after discussing with erikos

  • extensions/cpsection/network/view.py

    From 514158c32114e15d39dada82cdbe7641ab2306a7 Mon Sep 17 00:00:00 2001
    From: =?UTF-8?q?Manuel=20Qui=C3=B1ones?= <manuq@laptop.org>
    Date: Wed, 13 Mar 2013 21:24:11 -0300
    Subject: [PATCH] Add proxy configuration support to Network Control Panel - SL
     #3070
    MIME-Version: 1.0
    Content-Type: text/plain; charset=UTF-8
    Content-Transfer-Encoding: 8bit
    Mail-Followup-To: <sugar-devel@lists.sugarlabs.org>
    
    This implements the Proxy Settings Feature [1].
    
    Both individual users and deployments need to be able to set a proxy
    for Sugar and activities to use.  While we'd like the system to work
    that all out automatically (e.g. using WPAD [2]), this often isn't
    possible.  Common reasons include legacy ("inherited") setups and
    network uplinks simply being out of control of the user respectively
    deployment.
    
    The existing Network Control Panel is enhanced by adding a new section
    for the proxy settings.  For consistency between Sugar and Gnome, the
    basic layout of the Gnome 3 proxy settings has been mirrored: a combo
    box allows the user to select how the proxy setting should be
    determined (None=direct connection, Automatic=WPAD or PAC,
    Manual=enter host names and ports for each protocol).  Based on which
    method was selected, additional configuration options are presented to
    the user.
    
    Implementation:
    
    This patch is based in previous patches from Sascha Silbe [3] and Ajay
    Garg [4].  This one uses GSettings exclusively, not GConf.  The proxy
    settings are stored via GSettings using the same schemas as GNOME [5].
    
    It uses the convenient g_settings_bind() [6] to bind the widgets
    properties to the settings, instead of connecting callbacks to signals
    in the two directions.
    
    To implement undo, the settings are set to delay-apply mode [7].  An
    apply() method is added to the control panel SectionView, paired to
    the undo() method.  This new method is called by the GUI when the
    'accept' button is clicked.
    
    [1] https://wiki.sugarlabs.org/go/Features/Proxy_Settings
    [2] https://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol
    [3] http://lists.sugarlabs.org/archive/sugar-devel/2012-August/038806.html
    [4] http://lists.sugarlabs.org/archive/sugar-devel/2013-February/041779.html
    [5] https://developer.gnome.org/ProxyConfiguration/
    [6] https://developer.gnome.org/gio/stable/GSettings.html#g-settings-bind
    [7] https://developer.gnome.org/gio/stable/GSettings.html#g-settings-delay
    
    Signed-off-by: Manuel Quiñones <manuq@laptop.org>
    Signed-off-by: Ajay Garg <ajay at activitycentral.com>
    Signed-off-by: Sascha Silbe <silbe at activitycentral.com>
    Signed-off-by: Manuel Quiñones <manuq@laptop.org>
    ---
     extensions/cpsection/network/view.py   | 268 ++++++++++++++++++++++++++++++++-
     src/jarabe/controlpanel/gui.py         |   1 +
     src/jarabe/controlpanel/sectionview.py |   4 +
     3 files changed, 272 insertions(+), 1 deletion(-)
    
    diff --git a/extensions/cpsection/network/view.py b/extensions/cpsection/network/view.py
    index b360759..3c1ab79 100644
    a b  
    1313# You should have received a copy of the GNU General Public License
    1414# along with this program; if not, write to the Free Software
    1515# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
     16from gettext import gettext as _
    1617
    1718from gi.repository import Gtk
    1819from gi.repository import Gdk
    1920from gi.repository import GObject
    20 from gettext import gettext as _
     21from gi.repository import Gio
     22from gi.repository import Pango
    2123
    2224from sugar3.graphics import style
    2325
    TITLE = _('Network') 
    3234_APPLY_TIMEOUT = 3000
    3335
    3436
     37class SettingBox(Gtk.HBox):
     38    """
     39    Base class for "lines" on the screen representing configuration
     40    settings.
     41    """
     42    def __init__(self, name, size_group=None):
     43        Gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING)
     44        label = Gtk.Label(name)
     45        label.modify_fg(Gtk.StateType.NORMAL,
     46                        style.COLOR_SELECTION_GREY.get_gdk_color())
     47        label.set_alignment(1, 0.5)
     48        if size_group is not None:
     49            size_group.add_widget(label)
     50        self.pack_start(label, False, False, 0)
     51        label.show()
     52
     53
     54class ComboSettingBox(Gtk.VBox):
     55    """
     56    Container for sets of different settings selected by a top-level
     57    setting.
     58
     59    Renders the top level setting as a ComboBox.  Only the currently
     60    active set is shown on screen.
     61    """
     62    def __init__(self, name, setting, setting_key,
     63                 option_sets, size_group=None):
     64        Gtk.VBox.__init__(self, spacing=style.DEFAULT_SPACING)
     65
     66        setting_box = SettingBox(name, size_group)
     67        self.pack_start(setting_box, False, False, 0)
     68        setting_box.show()
     69
     70        display_sets = [(name, widget) for name, value, widget in option_sets]
     71
     72        model = Gtk.ListStore(str, str, object)
     73        combo_box = Gtk.ComboBox(model=model)
     74        combo_box.connect('changed', self.__combo_changed_cb)
     75        setting_box.pack_start(combo_box, True, True, 0)
     76        combo_box.show()
     77
     78        cell_renderer = Gtk.CellRendererText()
     79        cell_renderer.props.ellipsize = Pango.EllipsizeMode.MIDDLE
     80        cell_renderer.props.ellipsize_set = True
     81        combo_box.pack_start(cell_renderer, True)
     82        combo_box.add_attribute(cell_renderer, 'text', 0)
     83        combo_box.props.id_column = 1
     84
     85        self._settings_box = Gtk.VBox()
     86        self._settings_box.show()
     87        self.pack_start(self._settings_box, False, False, 0)
     88
     89        for optset in option_sets:
     90            model.append(optset)
     91
     92        setting.bind(setting_key, combo_box, 'active-id',
     93                     Gio.SettingsBindFlags.DEFAULT)
     94
     95    def __combo_changed_cb(self, combobox):
     96        giter = combobox.get_active_iter()
     97        new_box = combobox.get_model().get(giter, 2)[0]
     98        current_box = self._settings_box.get_children()
     99        if current_box:
     100            self._settings_box.remove(current_box[0])
     101
     102        self._settings_box.add(new_box)
     103        new_box.show()
     104
     105
     106class OptionalSettingsBox(Gtk.VBox):
     107    """
     108    Container for settings (de)activated by a top-level setting.
     109
     110    Renders the top level setting as a CheckButton. The settings are only
     111    shown on screen if the top-level setting is enabled.
     112    """
     113    def __init__(self, name, setting, setting_key, contents_box):
     114        Gtk.VBox.__init__(self, spacing=style.DEFAULT_SPACING)
     115
     116        check_button = Gtk.CheckButton()
     117        check_button.props.label = name
     118        check_button.connect('toggled', self.__button_toggled_cb, contents_box)
     119        check_button.show()
     120        self.pack_start(check_button, True, True, 0)
     121        self.pack_start(contents_box, False, False, 0)
     122
     123        setting.bind(setting_key, check_button, 'active',
     124                     Gio.SettingsBindFlags.DEFAULT)
     125
     126    def __button_toggled_cb(self, check_button, contents_box):
     127        contents_box.set_visible(check_button.get_active())
     128
     129
     130class HostPortSettingBox(SettingBox):
     131    """
     132    A configuration line for a combined host name and port setting.
     133    """
     134    def __init__(self, name, setting, size_group=None):
     135        SettingBox.__init__(self, name, size_group)
     136
     137        host_entry = Gtk.Entry()
     138        self.pack_start(host_entry, True, True, 0)
     139        host_entry.show()
     140
     141        setting.bind('host', host_entry, 'text', Gio.SettingsBindFlags.DEFAULT)
     142
     143        # port number 0 means n/a
     144        adjustment = Gtk.Adjustment(0, 0, 65535, 1, 10)
     145        port_spinbutton = Gtk.SpinButton(adjustment=adjustment, climb_rate=0.1)
     146        self.pack_start(port_spinbutton, False, False, 0)
     147        port_spinbutton.show()
     148
     149        setting.bind('port', port_spinbutton, 'value',
     150                     Gio.SettingsBindFlags.DEFAULT)
     151
     152
     153class StringSettingBox(SettingBox):
     154    """
     155    A configuration line for a string setting.
     156    """
     157    def __init__(self, name, setting, setting_key, size_group=None):
     158        SettingBox.__init__(self, name, size_group)
     159
     160        entry = Gtk.Entry()
     161        self.pack_start(entry, True, True, 0)
     162        entry.show()
     163
     164        setting.bind(setting_key, entry, 'text', Gio.SettingsBindFlags.DEFAULT)
     165
     166
    35167class Network(SectionView):
    36168    def __init__(self, model, alerts):
    37169        SectionView.__init__(self)
    class Network(SectionView): 
    44176        self._jabber_change_handler = None
    45177        self._radio_change_handler = None
    46178        self._network_configuration_reset_handler = None
     179        self._proxy_settings = {}
    47180
    48181        self.set_border_width(style.DEFAULT_SPACING * 2)
    49182        self.set_spacing(style.DEFAULT_SPACING)
    class Network(SectionView): 
    174307        workspace.pack_start(box_mesh, False, True, 0)
    175308        box_mesh.show()
    176309
     310        separator_proxy = Gtk.HSeparator()
     311        workspace.pack_start(separator_proxy, False, False, 0)
     312        separator_proxy.show()
     313
     314        self._add_proxy_section(workspace)
     315
    177316        self.setup()
    178317
     318    def _add_proxy_section(self, workspace):
     319        label_proxy = Gtk.Label(_('Proxy'))
     320        label_proxy.set_alignment(0, 0)
     321        workspace.pack_start(label_proxy, False, True, 0)
     322        label_proxy.show()
     323
     324        box_proxy = Gtk.VBox()
     325        box_proxy.set_border_width(style.DEFAULT_SPACING * 2)
     326        box_proxy.set_spacing(style.DEFAULT_SPACING)
     327        workspace.pack_start(box_proxy, False, True, 0)
     328        box_proxy.show()
     329
     330        # GSettings schemas for proxy:
     331        schemas = ['org.gnome.system.proxy',
     332                   'org.gnome.system.proxy.http',
     333                   'org.gnome.system.proxy.https',
     334                   'org.gnome.system.proxy.ftp',
     335                   'org.gnome.system.proxy.socks']
     336
     337        for schema in schemas:
     338            proxy_setting = Gio.Settings.new(schema)
     339
     340            # We are not going to apply the settings immediatly.
     341            # We'll apply them if the user presses the "accept"
     342            # button, or we'll revert them if the user presses the
     343            # "cancel" button.
     344            proxy_setting.delay()
     345
     346            self._proxy_settings[schema] = proxy_setting
     347
     348        size_group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
     349
     350        automatic_proxy_box = Gtk.VBox(spacing=style.DEFAULT_SPACING)
     351        manual_proxy_box = Gtk.VBox(spacing=style.DEFAULT_SPACING)
     352
     353        option_sets = [('None', 'none', Gtk.VBox()),
     354                       ('Manual', 'manual', manual_proxy_box),
     355                       ('Automatic', 'auto', automatic_proxy_box)]
     356
     357        box_mode = ComboSettingBox(
     358            _('Method:'), self._proxy_settings['org.gnome.system.proxy'],
     359            'mode', option_sets, size_group)
     360
     361        box_proxy.pack_start(box_mode, False, False, 0)
     362        box_mode.show()
     363
     364        url_box = StringSettingBox(
     365            _('Configuration URL:'),
     366            self._proxy_settings['org.gnome.system.proxy'], 'autoconfig-url',
     367            size_group)
     368
     369        automatic_proxy_box.pack_start(url_box, True, True, 0)
     370        url_box.show()
     371
     372        wpad_help_text = _('Web Proxy Autodiscovery is used when a'
     373                           ' Configuration URL is not provided. This is not'
     374                           ' recommended for untrusted public networks.')
     375        automatic_proxy_help = Gtk.Label(wpad_help_text)
     376        automatic_proxy_help.set_alignment(0, 0)
     377        automatic_proxy_help.set_line_wrap(True)
     378        automatic_proxy_help.show()
     379        automatic_proxy_box.pack_start(automatic_proxy_help, True, True, 0)
     380
     381        box_http = HostPortSettingBox(
     382            _('HTTP Proxy:'),
     383            self._proxy_settings['org.gnome.system.proxy.http'], size_group)
     384
     385        manual_proxy_box.pack_start(box_http, False, False, 0)
     386        box_http.show()
     387
     388        auth_contents_box = Gtk.VBox(spacing=style.DEFAULT_SPACING)
     389
     390        auth_box = OptionalSettingsBox(
     391            _('Use authentication'),
     392            self._proxy_settings['org.gnome.system.proxy.http'],
     393            'use-authentication', auth_contents_box)
     394
     395        manual_proxy_box.pack_start(auth_box, False, False, 0)
     396        auth_box.show()
     397
     398        proxy_http_setting = Gio.Settings.new('org.gnome.system.proxy.http')
     399        proxy_http_setting.delay()
     400
     401        box_username = StringSettingBox(
     402            _('Username:'),
     403            self._proxy_settings['org.gnome.system.proxy.http'],
     404            'authentication-user', size_group)
     405
     406        auth_contents_box.pack_start(box_username, False, False, 0)
     407        box_username.show()
     408
     409        box_password = StringSettingBox(
     410            _('Password:'),
     411            self._proxy_settings['org.gnome.system.proxy.http'],
     412            'authentication-password', size_group)
     413
     414        auth_contents_box.pack_start(box_password, False, False, 0)
     415        box_password.show()
     416
     417        box_https = HostPortSettingBox(
     418            _('HTTPS Proxy:'),
     419            self._proxy_settings['org.gnome.system.proxy.https'], size_group)
     420
     421        manual_proxy_box.pack_start(box_https, False, False, 0)
     422        box_https.show()
     423
     424        box_ftp = HostPortSettingBox(
     425            _('FTP Proxy:'),
     426            self._proxy_settings['org.gnome.system.proxy.ftp'],
     427            size_group)
     428
     429        manual_proxy_box.pack_start(box_ftp, False, False, 0)
     430        box_ftp.show()
     431
     432        box_socks = HostPortSettingBox(
     433            _('SOCKS Proxy:'),
     434            self._proxy_settings['org.gnome.system.proxy.socks'], size_group)
     435
     436        manual_proxy_box.pack_start(box_socks, False, False, 0)
     437        box_socks.show()
     438
    179439    def setup(self):
    180440        self._entry.set_text(self._model.get_jabber())
    181441        try:
    class Network(SectionView): 
    203463        self._model.undo()
    204464        self._jabber_alert.hide()
    205465        self._radio_alert.hide()
     466        for setting in self._proxy_settings.values():
     467            setting.revert()
     468
     469    def apply(self):
     470        for setting in self._proxy_settings.values():
     471            setting.apply()
    206472
    207473    def _validate(self):
    208474        if self._jabber_valid and self._radio_valid:
  • src/jarabe/controlpanel/gui.py

    diff --git a/src/jarabe/controlpanel/gui.py b/src/jarabe/controlpanel/gui.py
    index c61fb4e..f358bf9 100644
    a b class ControlPanel(Gtk.Window): 
    318318        self._show_main_view()
    319319
    320320    def __accept_clicked_cb(self, widget):
     321        self._section_view.apply()
    321322        if self._section_view.needs_restart:
    322323            self._section_toolbar.accept_button.set_sensitive(False)
    323324            self._section_toolbar.cancel_button.set_sensitive(False)
  • src/jarabe/controlpanel/sectionview.py

    diff --git a/src/jarabe/controlpanel/sectionview.py b/src/jarabe/controlpanel/sectionview.py
    index cbf4768..cfc58d0 100644
    a b class SectionView(Gtk.VBox): 
    5252    def undo(self):
    5353        """Undo here the changes that have been made in this section."""
    5454        pass
     55
     56    def apply(self):
     57        """Apply here the changes that have been made in this section."""
     58        pass