diff --git a/gajim/data/gui/change_password_dialog.ui b/gajim/data/gui/change_password_dialog.ui deleted file mode 100644 index 292334a4fb9e931cb0e17c535a8ec0ef2f450b2b..0000000000000000000000000000000000000000 --- a/gajim/data/gui/change_password_dialog.ui +++ /dev/null @@ -1,56 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- Generated with glade 3.22.1 --> -<interface> - <requires lib="gtk+" version="3.12"/> - <object class="GtkBox" id="change_password_box"> - <property name="width_request">220</property> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="margin_bottom">12</property> - <property name="orientation">vertical</property> - <property name="spacing">6</property> - <child> - <object class="GtkEntry" id="password1_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="visibility">False</property> - <property name="placeholder_text" translatable="yes">New Password</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">0</property> - </packing> - </child> - <child> - <object class="GtkEntry" id="password2_entry"> - <property name="visible">True</property> - <property name="can_focus">True</property> - <property name="visibility">False</property> - <property name="activates_default">True</property> - <property name="placeholder_text" translatable="yes">Confirm New Password</property> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">1</property> - </packing> - </child> - <child> - <object class="GtkLabel" id="error_label"> - <property name="visible">True</property> - <property name="can_focus">False</property> - <property name="wrap">True</property> - <property name="xalign">0</property> - <style> - <class name="error-color"/> - </style> - </object> - <packing> - <property name="expand">False</property> - <property name="fill">True</property> - <property name="position">2</property> - </packing> - </child> - </object> -</interface> diff --git a/gajim/gtk/change_password.py b/gajim/gtk/change_password.py new file mode 100644 index 0000000000000000000000000000000000000000..ee0d85d789c9518102fb51f232d36cc85232f106 --- /dev/null +++ b/gajim/gtk/change_password.py @@ -0,0 +1,242 @@ +# This file is part of Gajim. +# +# Gajim is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; version 3 only. +# +# Gajim is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Gajim. If not, see <http://www.gnu.org/licenses/>. + +import logging + +from gi.repository import Gtk + +from nbxmpp.util import is_error_result + +from gajim.common import app +from gajim.common.i18n import _ +from gajim.common import passwords +from gajim.common.helpers import to_user_string + +from gajim.gtk.assistant import Assistant +from gajim.gtk.assistant import Page +from gajim.gtk.dataform import DataFormWidget +from gajim.gtk.util import ensure_not_destroyed + +log = logging.getLogger('gajim.gtk.change_password') + + +class ChangePassword(Assistant): + def __init__(self, account): + Assistant.__init__(self) + + self.account = account + self._con = app.connections.get(account) + self._destroyed = False + + self.add_button('apply', _('Change'), 'suggested-action', + complete=True) + self.add_button('close', _('Close')) + self.add_button('back', _('Back')) + + self.add_pages({'password': EnterPassword(), + 'next_stage': NextStage()}) + + progress = self.add_default_page('progress') + progress.set_title(_('Changing Password...')) + progress.set_text(_('Trying to change password...')) + + success = self.add_default_page('success') + success.set_title(_('Password Changed')) + success.set_heading(_('Password Changed')) + success.set_text(_('Your password has successfully been changed.')) + + error = self.add_default_page('error') + error.set_title(_('Password Change Failed')) + error.set_heading(_('Password Change Failed')) + error.set_text( + _('An error occured while trying to change your password.')) + + self.set_button_visible_func(self._visible_func) + + self.connect('button-clicked', self._on_button_clicked) + self.connect('page-changed', self._on_page_changed) + self.connect('destroy', self._on_destroy) + + self.show_all() + + @staticmethod + def _visible_func(_assistant, page_name): + if page_name in ('password', 'next_stage'): + return ['apply'] + + if page_name == 'progress': + return None + + if page_name == 'error': + return ['back'] + + if page_name == 'success': + return ['close'] + raise ValueError('page %s unknown' % page_name) + + def _on_button_clicked(self, _assistant, button_name): + page = self.get_current_page() + if button_name == 'apply': + self.show_page('progress', Gtk.StackTransitionType.SLIDE_LEFT) + self._on_apply(next_stage=page == 'next_stage') + + elif button_name == 'back': + self.show_page('password', Gtk.StackTransitionType.SLIDE_RIGHT) + + elif button_name == 'close': + self.destroy() + + def _on_page_changed(self, _assistant, page_name): + if page_name in ('password', 'next_stage'): + self.set_default_button('apply') + + elif page_name == 'success': + password = self.get_page('password').get_password() + passwords.save_password(self.account, password) + self.set_default_button('close') + + elif page_name == 'error': + self.set_default_button('back') + + def _on_apply(self, next_stage=False): + register = self._con.connection.get_module('Register') + if next_stage: + form = self.get_page('next_stage').get_submit_form() + register.change_password_with_form( + form, callback=self._on_change_password) + else: + password = self.get_page('password').get_password() + register.change_password(password, + callback=self._on_change_password) + + @ensure_not_destroyed + def _on_change_password(self, result): + if is_error_result(result): + error_text = to_user_string(result) + self.get_page('error').set_text(error_text) + self.show_page('error', Gtk.StackTransitionType.SLIDE_LEFT) + elif result.successful: + self.show_page('success') + else: + self.get_page('next_stage').set_form(result.form) + self.show_page('next_stage', Gtk.StackTransitionType.SLIDE_LEFT) + + def _on_destroy(self, *args): + self._destroyed = True + + +class EnterPassword(Page): + def __init__(self): + Page.__init__(self) + self.complete = False + self.title = _('Change Password') + + heading = Gtk.Label(label=_('Change Password')) + heading.get_style_context().add_class('large-header') + heading.set_max_width_chars(30) + heading.set_line_wrap(True) + heading.set_halign(Gtk.Align.CENTER) + heading.set_justify(Gtk.Justification.CENTER) + + label = Gtk.Label(label=_('Please enter your new password.')) + label.set_max_width_chars(50) + label.set_line_wrap(True) + label.set_halign(Gtk.Align.CENTER) + label.set_justify(Gtk.Justification.CENTER) + label.set_margin_bottom(12) + + self._password1_entry = Gtk.Entry() + self._password1_entry.set_input_purpose(Gtk.InputPurpose.PASSWORD) + self._password1_entry.set_visibility(False) + self._password1_entry.set_invisible_char('•') + self._password1_entry.set_valign(Gtk.Align.END) + self._password1_entry.set_placeholder_text( + _('Enter new password...')) + self._password1_entry.connect('changed', self._on_changed) + self._password2_entry = Gtk.Entry() + self._password2_entry.set_input_purpose(Gtk.InputPurpose.PASSWORD) + self._password2_entry.set_visibility(False) + self._password2_entry.set_invisible_char('•') + self._password2_entry.set_activates_default(True) + self._password2_entry.set_valign(Gtk.Align.START) + self._password2_entry.set_placeholder_text( + _('Confirm new password...')) + self._password2_entry.connect('changed', self._on_changed) + + self.pack_start(heading, False, True, 0) + self.pack_start(label, False, True, 0) + self.pack_start(self._password1_entry, True, True, 0) + self.pack_start(self._password2_entry, True, True, 0) + self._show_icon(False) + self.show_all() + + def _show_icon(self, show): + if show: + self._password2_entry.set_icon_from_icon_name( + Gtk.EntryIconPosition.SECONDARY, 'dialog-warning-symbolic') + self._password2_entry.set_icon_tooltip_text( + Gtk.EntryIconPosition.SECONDARY, _('Passwords do not match')) + else: + self._password2_entry.set_icon_from_icon_name( + Gtk.EntryIconPosition.SECONDARY, None) + + def _on_changed(self, _entry): + password1 = self._password1_entry.get_text() + if not password1: + self._show_icon(True) + self._set_complete(False) + return + + password2 = self._password2_entry.get_text() + if password1 != password2: + self._show_icon(True) + self._set_complete(False) + return + self._show_icon(False) + self._set_complete(True) + + def _set_complete(self, state): + self.complete = state + self.get_toplevel().update_page_complete() + + def get_password(self): + return self._password1_entry.get_text() + + +class NextStage(Page): + def __init__(self): + Page.__init__(self) + self.set_valign(Gtk.Align.FILL) + self.complete = False + self.title = _('Change Password') + self._current_form = None + + self.show_all() + + def _on_is_valid(self, _widget, is_valid): + self.complete = is_valid + self.get_toplevel().update_page_complete() + + def set_form(self, form): + if self._current_form is not None: + self.remove(self._current_form) + self._current_form.destroy() + self._current_form = DataFormWidget(form) + self._current_form.connect('is-valid', self._on_is_valid) + self._current_form.validate() + self.pack_start(self._current_form, True, True, 0) + self._current_form.show_all() + + def get_submit_form(self): + return self._current_form.get_submit_form() diff --git a/gajim/gtk/const.py b/gajim/gtk/const.py index 3dd001eb12a1dea839b9bc0e1cdfab2de7d2987a..6eff760e93b2997cfb0d7cc291a363803e535b57 100644 --- a/gajim/gtk/const.py +++ b/gajim/gtk/const.py @@ -130,4 +130,5 @@ def __str__(self): 'CertificateDialog': 'gajim.gtk.dialogs', 'SubscriptionRequest': 'gajim.gtk.subscription_request', 'RemoveAccount': 'gajim.gtk.remove_account', + 'ChangePassword': 'gajim.gtk.change_password', } diff --git a/gajim/gtk/dialogs.py b/gajim/gtk/dialogs.py index af277ae5a985f3c94ef6a0ccb320fa9072c6b43c..10e9d0f854862e2fdf6bafe52050cf067b359952 100644 --- a/gajim/gtk/dialogs.py +++ b/gajim/gtk/dialogs.py @@ -258,59 +258,6 @@ def _on_copy_cert_info_button_clicked(self, widget): self._clipboard.set_text(clipboard_text, -1) -class ChangePasswordDialog(Gtk.Dialog): - def __init__(self, account, success_cb, transient_for): - super().__init__(title=_('Change Password'), - transient_for=transient_for, - destroy_with_parent=True) - - self._account = account - self._success_cb = success_cb - - self._builder = get_builder('change_password_dialog.ui') - self.get_content_area().add( - self._builder.get_object('change_password_box')) - self._password1_entry = self._builder.get_object('password1_entry') - self._password2_entry = self._builder.get_object('password2_entry') - self._error_label = self._builder.get_object('error_label') - - self.add_button(_('_OK'), Gtk.ResponseType.OK) - self.set_default_response(Gtk.ResponseType.OK) - self.get_style_context().add_class('dialog-margin') - self.connect('response', self._on_dialog_response) - self.show_all() - - def _on_dialog_response(self, dialog, response): - if response != Gtk.ResponseType.OK: - self.destroy() - return - - password1 = self._password1_entry.get_text() - if not password1: - self._error_label.set_text(_('You must enter a password')) - return - password2 = self._password2_entry.get_text() - if password1 != password2: - self._error_label.set_text(_('Passwords do not match')) - return - - self._password1_entry.set_sensitive(False) - self._password2_entry.set_sensitive(False) - - con = app.connections[self._account] - con.get_module('Register').change_password( - password1, self._on_success, self._on_error) - - def _on_success(self): - self._success_cb(self._password1_entry.get_text()) - self.destroy() - - def _on_error(self, error_text): - self._error_label.set_text(error_text) - self._password1_entry.set_sensitive(True) - self._password2_entry.set_sensitive(True) - - class InvitationReceivedDialog(Gtk.ApplicationWindow): def __init__(self, account, event): Gtk.ApplicationWindow.__init__(self) diff --git a/gajim/gtk/settings.py b/gajim/gtk/settings.py index d34a7710739d2adc2f693b14d5f71e5730793be5..628981248b6af4517f32ce282a7091e600c588ec 100644 --- a/gajim/gtk/settings.py +++ b/gajim/gtk/settings.py @@ -28,9 +28,9 @@ from gajim import gtkgui_helpers -from gajim.gtk.dialogs import ChangePasswordDialog from gajim.gtk.util import get_image_button from gajim.gtk.util import MaxWidthComboBoxText +from gajim.gtk.util import open_window from gajim.gtk.const import SettingKind from gajim.gtk.const import SettingType @@ -635,18 +635,10 @@ def get_setting_value(self): class ChangePasswordSetting(DialogSetting): def __init__(self, *args, **kwargs): DialogSetting.__init__(self, *args, **kwargs) - self.change_dialog = None def show_dialog(self, parent): - try: - self.change_dialog = ChangePasswordDialog( - self.account, self.on_changed, parent) - except GajimGeneralException: - return - self.change_dialog.set_modal(True) - - def on_changed(self, new_password): - self.set_value(new_password) + parent.destroy() + open_window('ChangePassword', account=self.account) def update_activatable(self, name, value): activatable = False diff --git a/test/gtk/change_password.py b/test/gtk/change_password.py new file mode 100644 index 0000000000000000000000000000000000000000..6cabf9ae20a763f17f090f8c83209f106a0cdad0 --- /dev/null +++ b/test/gtk/change_password.py @@ -0,0 +1,37 @@ +from functools import partial + +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk + +from nbxmpp.modules.dataforms import create_field +from nbxmpp.modules.dataforms import SimpleDataForm + +from gajim.common.const import CSSPriority + +from gajim.gtk.change_password import ChangePassword + +from test.gtk import util +util.load_style('gajim.css', CSSPriority.APPLICATION) + +fields = [ + create_field(typ='text-single', label='Username', var='username'), + create_field(typ='text-single', label='Old Password', var='old_password'), + create_field(typ='text-single', label='Mothers name', var='mother', required=True), +] + +form = SimpleDataForm(type_='form', fields=fields) + +def _apply(self, next_stage=False): + if next_stage: + print(self.get_page('next_stage').get_submit_form()) + else: + self.get_page('next_stage').set_form(form) + self.show_page('next_stage', Gtk.StackTransitionType.SLIDE_LEFT) + +win = ChangePassword(None) +win._on_apply = partial(_apply, win) + +win.connect('destroy', Gtk.main_quit) +win.show_all() +Gtk.main()