diff options
Diffstat (limited to 'src/leap/bitmask/gui/preferenceswindow.py')
| -rw-r--r-- | src/leap/bitmask/gui/preferenceswindow.py | 505 | 
1 files changed, 122 insertions, 383 deletions
| diff --git a/src/leap/bitmask/gui/preferenceswindow.py b/src/leap/bitmask/gui/preferenceswindow.py index 3c9cd5d0..f1252301 100644 --- a/src/leap/bitmask/gui/preferenceswindow.py +++ b/src/leap/bitmask/gui/preferenceswindow.py @@ -20,428 +20,167 @@ Preferences window  """  import logging -from functools import partial -  from PySide import QtCore, QtGui -from leap.bitmask.config.leapsettings import LeapSettings +from leap.bitmask.services import EIP_SERVICE, MX_SERVICE +  from leap.bitmask.gui.ui_preferences import Ui_Preferences -from leap.bitmask.util.credentials import password_checks -from leap.bitmask.services import get_service_display_name, MX_SERVICE +from leap.bitmask.gui.preferences_account_page import PreferencesAccountPage +from leap.bitmask.gui.preferences_vpn_page import PreferencesVpnPage +from leap.bitmask.gui.preferences_email_page import PreferencesEmailPage  logger = logging.getLogger(__name__)  class PreferencesWindow(QtGui.QDialog): +      """      Window that displays the preferences.      """ -    preferences_saved = QtCore.Signal() -    def __init__(self, parent, username, domain, backend, soledad_started, mx, -                 leap_signaler): +    _current_window = None  # currently visible preferences window + +    def __init__(self, parent, account, app):          """          :param parent: parent object of the PreferencesWindow.          :parent type: QWidget -        :param username: the user set in the login widget -        :type username: unicode -        :param domain: the selected domain in the login widget -        :type domain: unicode -        :param backend: Backend being used -        :type backend: Backend -        :param soledad_started: whether soledad has started or not -        :type soledad_started: bool -        :param mx: whether the current provider provides mx or not. -        :type mx: bool + +        :param account: the user or provider +        :type account: Account + +        :param app: the current App object +        :type app: App          """          QtGui.QDialog.__init__(self, parent) -        self.AUTOMATIC_GATEWAY_LABEL = self.tr("Automatic") - -        self._username = username -        self._domain = domain -        self._leap_signaler = leap_signaler -        self._backend = backend -        self._soledad_started = soledad_started -        self._mx_provided = mx -        self._settings = LeapSettings() -        self._backend_connect() +        self.account = account +        self.app = app -        # Load UI          self.ui = Ui_Preferences()          self.ui.setupUi(self) -        self.ui.lblPasswordChangeStatus.setVisible(False) -        self.ui.lblProvidersServicesStatus.setVisible(False) -        self._selected_services = set() - -        # Connections -        self.ui.pbChangePassword.clicked.connect(self._change_password) -        self.ui.cbProvidersServices.currentIndexChanged[unicode].connect( -            self._populate_services) - -        if not self._settings.get_configured_providers(): -            self.ui.gbEnabledServices.setEnabled(False) -        else: -            self._add_configured_providers() - -        if self._username is None: -            self._not_logged_in() -        else: -            self.ui.gbPasswordChange.setEnabled(True) -            if self._mx_provided: -                self._provides_mx() - -        self._select_provider_by_name(domain) - -    def _not_logged_in(self): -        """ -        Actions to perform if the user is not logged in. -        """ -        msg = self.tr( -            "In order to change your password you need to be logged in.") -        self._set_password_change_status(msg) -        self.ui.gbPasswordChange.setEnabled(False) - -    def _provides_mx(self): -        """ -        Actions to perform if the provider provides MX. -        """ -        pw_enabled = True -        enabled_services = self._settings.get_enabled_services(self._domain) -        mx_name = get_service_display_name(MX_SERVICE) - -        if MX_SERVICE not in enabled_services: -            msg = self.tr("You need to enable {0} in order to change " -                          "the password.".format(mx_name)) -            self._set_password_change_status(msg, error=True) -            pw_enabled = False -        else: -            # check if Soledad is bootstrapped -            if not self._soledad_started: -                msg = self.tr( -                    "You need to wait until {0} is ready in " -                    "order to change the password.".format(mx_name)) -                self._set_password_change_status(msg) -                pw_enabled = False - -        self.ui.gbPasswordChange.setEnabled(pw_enabled) - -    @QtCore.Slot() -    def set_soledad_ready(self): +        self.ui.close_button.clicked.connect(self.close) +        self.ui.account_label.setText(account.address) + +        self.app.service_selection_changed.connect(self._update_icons) + +        self._add_icons() +        self._add_pages() +        self._update_icons(self.account, self.account.services()) + +        # only allow a single preferences window at a time. +        if PreferencesWindow._current_window is not None: +            PreferencesWindow._current_window.close_window() +        PreferencesWindow._current_window = self + +    def _add_icons(self): +        """ +        Adds all the icons for the different configuration categories. +        Icons are QListWidgetItems added to the nav_widget on the side +        of the preferences window. + +        A note on sizing of QListWidgetItems +          icon_width = list_widget.width - (2 x nav_widget.spacing) - 2 +          icon_height = 56 seems to look ok +        """ +        account_item = QtGui.QListWidgetItem(self.ui.nav_widget) +        account_item.setIcon(QtGui.QIcon(":/images/black/32/user.png")) +        account_item.setText(self.tr("Account")) +        account_item.setTextAlignment( +            QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) +        account_item.setFlags( +            QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) +        account_item.setSizeHint(QtCore.QSize(98, 56)) +        self._account_item = account_item + +        vpn_item = QtGui.QListWidgetItem(self.ui.nav_widget) +        vpn_item.setHidden(True) +        vpn_item.setIcon(QtGui.QIcon(":/images/black/32/earth.png")) +        vpn_item.setText(self.tr("VPN")) +        vpn_item.setTextAlignment( +            QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) +        vpn_item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) +        vpn_item.setSizeHint(QtCore.QSize(98, 56)) +        self._vpn_item = vpn_item + +        email_item = QtGui.QListWidgetItem(self.ui.nav_widget) +        email_item.setHidden(True) +        email_item.setIcon(QtGui.QIcon(":/images/black/32/email.png")) +        email_item.setText(self.tr("Email")) +        email_item.setTextAlignment( +            QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter) +        email_item.setFlags( +            QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) +        email_item.setSizeHint(QtCore.QSize(98, 56)) +        self._email_item = email_item + +        self.ui.nav_widget.currentItemChanged.connect(self._change_page) +        self.ui.nav_widget.setCurrentRow(0) + +    def _add_pages(self): +        """ +        Adds the pages for the different configuration categories. +        """ +        self._account_page = PreferencesAccountPage(self, self.account, self.app) +        self._vpn_page = PreferencesVpnPage(self, self.account, self.app) +        self._email_page = PreferencesEmailPage(self, self.account, self.app) + +        self.ui.pages_widget.addWidget(self._account_page) +        self.ui.pages_widget.addWidget(self._vpn_page) +        self.ui.pages_widget.addWidget(self._email_page) + +    # +    # Slots +    # + +    def closeEvent(self, e):          """          TRIGGERS: -            parent.soledad_ready +            self.ui.close_button.clicked +              (since self.close() will trigger closeEvent) +            whenever the window is closed -        It notifies when the soledad object as ready to use. +        Close this dialog and destroy it.          """ -        self.ui.lblPasswordChangeStatus.setVisible(False) -        self.ui.gbPasswordChange.setEnabled(True) +        PreferencesWindow._current_window = None -    def _set_password_change_status(self, status, error=False, success=False): -        """ -        Sets the status label for the password change. - -        :param status: status message to display, can be HTML -        :type status: str -        """ -        if error: -            status = "<font color='red'><b>%s</b></font>" % (status,) -        elif success: -            status = "<font color='green'><b>%s</b></font>" % (status,) - -        if not self.ui.gbPasswordChange.isEnabled(): -            status = "<font color='black'>%s</font>" % (status,) - -        self.ui.lblPasswordChangeStatus.setVisible(True) -        self.ui.lblPasswordChangeStatus.setText(status) - -    def _set_changing_password(self, disable): -        """ -        Enables or disables the widgets in the password change group box. - -        :param disable: True if the widgets should be disabled and -                        it displays the status label that shows that is -                        changing the password. -                        False if they should be enabled. -        :type disable: bool -        """ -        if disable: -            self._set_password_change_status(self.tr("Changing password...")) - -        self.ui.leCurrentPassword.setEnabled(not disable) -        self.ui.leNewPassword.setEnabled(not disable) -        self.ui.leNewPassword2.setEnabled(not disable) -        self.ui.pbChangePassword.setEnabled(not disable) +        # deleteLater does not seem to cascade to items in stackLayout +        # (even with QtCore.Qt.WA_DeleteOnClose attribute). +        # so, here we call deleteLater() explicitly: +        self._account_page.deleteLater() +        self._vpn_page.deleteLater() +        self._email_page.deleteLater() +        self.deleteLater()      @QtCore.Slot() -    def _change_password(self): +    def _change_page(self, current, previous):          """          TRIGGERS: -            self.ui.pbChangePassword.clicked - -        Changes the user's password if the inputboxes are correctly filled. -        """ -        username = self._username -        current_password = self.ui.leCurrentPassword.text() -        new_password = self.ui.leNewPassword.text() -        new_password2 = self.ui.leNewPassword2.text() - -        ok, msg = password_checks(username, new_password, new_password2) - -        if not ok: -            self._set_changing_password(False) -            self._set_password_change_status(msg, error=True) -            self.ui.leNewPassword.setFocus() -            return - -        self._set_changing_password(True) -        self._backend.user_change_password(current_password=current_password, -                                           new_password=new_password) - -    @QtCore.Slot() -    def _srp_change_password_ok(self): -        """ -        TRIGGERS: -            self._backend.signaler.srp_password_change_ok - -        Callback used to display a successfully changed password. -        """ -        new_password = self.ui.leNewPassword.text() -        logger.debug("SRP password changed successfully.") - -        if self._mx_provided: -            self._backend.soledad_change_password(new_password=new_password) -        else: -            self._change_password_success() +            self.ui.nav_widget.currentItemChanged -    @QtCore.Slot(unicode) -    def _srp_change_password_problem(self, msg): -        """ -        TRIGGERS: -            self._backend.signaler.srp_password_change_error -            self._backend.signaler.srp_password_change_badpw +        Changes what page is displayed. -        Callback used to display an error on changing password. +        :param current: the currently selected item (might be None?) +        :type current: PySide.QtGui.QListWidgetItem -        :param msg: the message to show to the user. -        :type msg: unicode +        :param previous: the previously selected item (might be None) +        :type previous: PySide.QtGui.QListWidgetItem          """ -        logger.error("Error changing password") -        self._set_password_change_status(msg, error=True) -        self._set_changing_password(False) +        if not current: +            current = previous +        self.ui.pages_widget.setCurrentIndex(self.ui.nav_widget.row(current)) -    @QtCore.Slot() -    def _soledad_change_password_ok(self): +    @QtCore.Slot(object, list) +    def _update_icons(self, account, services):          """          TRIGGERS: -            Signaler.soledad_password_change_ok - -        Soledad password change went OK. -        """ -        logger.debug("Soledad password changed successfully.") -        self._change_password_success() +            self.app.service_selection_changed -    def _change_password_success(self): +        Change which icons are visible.          """ -        Callback used to display a successfully changed password. -        """ -        logger.debug("Soledad password changed successfully.") - -        self._set_password_change_status( -            self.tr("Password changed successfully."), success=True) -        self._clear_password_inputs() -        self._set_changing_password(False) - -    @QtCore.Slot(unicode) -    def _soledad_change_password_problem(self, msg): -        """ -        TRIGGERS: -            Signaler.soledad_password_change_error - -        Callback used to display an error on changing password. - -        :param msg: the message to show to the user. -        :type msg: unicode -        """ -        logger.error("Error changing soledad password") -        self._set_password_change_status(msg, error=True) -        self._set_changing_password(False) - -    def _clear_password_inputs(self): -        """ -        Clear the contents of the inputs. -        """ -        self.ui.leCurrentPassword.setText("") -        self.ui.leNewPassword.setText("") -        self.ui.leNewPassword2.setText("") - -    def _set_providers_services_status(self, status, success=False): -        """ -        Sets the status label for the password change. - -        :param status: status message to display, can be HTML -        :type status: str -        :param success: is set to True if we should display the -                        message as green -        :type success: bool -        """ -        if success: -            status = "<font color='green'><b>%s</b></font>" % (status,) - -        self.ui.lblProvidersServicesStatus.setVisible(True) -        self.ui.lblProvidersServicesStatus.setText(status) - -    def _add_configured_providers(self): -        """ -        Add the client's configured providers to the providers combo boxes. -        """ -        self.ui.cbProvidersServices.clear() -        for provider in self._settings.get_configured_providers(): -            self.ui.cbProvidersServices.addItem(provider) - -    def _select_provider_by_name(self, name): -        """ -        Given a provider name/domain, selects it in the combobox. - -        :param name: name or domain for the provider -        :type name: str -        """ -        provider_index = self.ui.cbProvidersServices.findText(name) -        self.ui.cbProvidersServices.setCurrentIndex(provider_index) - -    @QtCore.Slot(str, int) -    def _service_selection_changed(self, service, state): -        """ -        TRIGGERS: -            service_checkbox.stateChanged - -        Adds the service to the state if the state is checked, removes -        it otherwise - -        :param service: service to handle -        :type service: str -        :param state: state of the checkbox -        :type state: int -        """ -        if state == QtCore.Qt.Checked: -            self._selected_services = \ -                self._selected_services.union(set([service])) -        else: -            self._selected_services = \ -                self._selected_services.difference(set([service])) - -        # We hide the maybe-visible status label after a change -        self.ui.lblProvidersServicesStatus.setVisible(False) - -    @QtCore.Slot(str) -    def _populate_services(self, domain): -        """ -        TRIGGERS: -            self.ui.cbProvidersServices.currentIndexChanged[unicode] - -        Fill the services list with the selected provider's services. - -        :param domain: the domain of the provider to load services from. -        :type domain: str -        """ -        # We hide the maybe-visible status label after a change -        self.ui.lblProvidersServicesStatus.setVisible(False) - -        if not domain: +        if account != self.account:              return -        # set the proper connection for the 'save' button -        try: -            self.ui.pbSaveServices.clicked.disconnect() -        except RuntimeError: -            pass  # Signal was not connected - -        save_services = partial(self._save_enabled_services, domain) -        self.ui.pbSaveServices.clicked.connect(save_services) - -        self._backend.provider_get_supported_services(domain=domain) - -    @QtCore.Slot(str) -    def _load_services(self, services): -        """ -        TRIGGERS: -            self.ui.cbProvidersServices.currentIndexChanged[unicode] - -        Loads the services that the provider provides into the UI for -        the user to enable or disable. - -        :param domain: the domain of the provider to load services from. -        :type domain: str -        """ -        domain = self.ui.cbProvidersServices.currentText() -        services_conf = self._settings.get_enabled_services(domain) - -        # discard changes if other provider is selected -        self._selected_services = set() - -        # from: http://stackoverflow.com/a/13103617/687989 -        # remove existing checkboxes -        layout = self.ui.vlServices -        for i in reversed(range(layout.count())): -            layout.itemAt(i).widget().setParent(None) - -        # add one checkbox per service and set the current configured value -        for service in services: -            try: -                checkbox = QtGui.QCheckBox(self) -                service_label = get_service_display_name(service) -                checkbox.setText(service_label) - -                self.ui.vlServices.addWidget(checkbox) -                checkbox.stateChanged.connect( -                    partial(self._service_selection_changed, service)) - -                checkbox.setChecked(service in services_conf) -            except ValueError: -                logger.error("Something went wrong while trying to " -                             "load service %s" % (service,)) - -    @QtCore.Slot(str) -    def _save_enabled_services(self, provider): -        """ -        TRIGGERS: -            self.ui.pbSaveServices.clicked - -        Saves the new enabled services settings to the configuration file. - -        :param provider: the provider config that we need to save. -        :type provider: str -        """ -        services = list(self._selected_services) -        self._settings.set_enabled_services(provider, services) - -        msg = self.tr( -            "Services settings for provider '{0}' saved.".format(provider)) -        logger.debug(msg) -        self._set_providers_services_status(msg, success=True) -        self.preferences_saved.emit() - -    def _backend_connect(self): -        """ -        Helper to connect to backend signals -        """ -        sig = self._leap_signaler - -        sig.prov_get_supported_services.connect(self._load_services) - -        sig.srp_password_change_ok.connect(self._srp_change_password_ok) - -        pwd_change_error = lambda: self._srp_change_password_problem( -            self.tr("There was a problem changing the password.")) -        sig.srp_password_change_error.connect(pwd_change_error) - -        pwd_change_badpw = lambda: self._srp_change_password_problem( -            self.tr("You did not enter a correct current password.")) -        sig.srp_password_change_badpw.connect(pwd_change_badpw) - -        sig.soledad_password_change_ok.connect( -            self._soledad_change_password_ok) - -        sig.soledad_password_change_error.connect( -            self._soledad_change_password_problem) +        self._vpn_item.setHidden(not EIP_SERVICE in services) +        # self._email_item.setHidden(not MX_SERVICE in services) +        # ^^ disable email for now, there is nothing there yet. | 
