comparison libervia/frontends/quick_frontend/quick_profile_manager.py @ 4074:26b7ed2817da

refactoring: rename `sat_frontends` to `libervia.frontends`
author Goffi <goffi@goffi.org>
date Fri, 02 Jun 2023 14:12:38 +0200
parents sat_frontends/quick_frontend/quick_profile_manager.py@4b842c1fb686
children b620a8e882e1
comparison
equal deleted inserted replaced
4073:7c5654c54fed 4074:26b7ed2817da
1 #!/usr/bin/env python3
2
3
4 # helper class for making a SAT frontend
5 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
6
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
16
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
20 from libervia.backend.core.i18n import _
21 from libervia.backend.core import log as logging
22
23 log = logging.getLogger(__name__)
24 from libervia.frontends.primitivus.constants import Const as C
25
26
27 class ProfileRecord(object):
28 """Class which manage data for one profile"""
29
30 def __init__(self, profile=None, login=None, password=None):
31 self._profile = profile
32 self._login = login
33 self._password = password
34
35 @property
36 def profile(self):
37 return self._profile
38
39 @profile.setter
40 def profile(self, value):
41 self._profile = value
42 # if we change the profile,
43 # we must have no login/password until backend give them
44 self._login = self._password = None
45
46 @property
47 def login(self):
48 return self._login
49
50 @login.setter
51 def login(self, value):
52 self._login = value
53
54 @property
55 def password(self):
56 return self._password
57
58 @password.setter
59 def password(self, value):
60 self._password = value
61
62
63 class QuickProfileManager(object):
64 """Class with manage profiles creation/deletion/connection"""
65
66 def __init__(self, host, autoconnect=None):
67 """Create the manager
68
69 @param host: %(doc_host)s
70 @param autoconnect(iterable): list of profiles to connect automatically
71 """
72 self.host = host
73 self._autoconnect = bool(autoconnect)
74 self.current = ProfileRecord()
75
76 def go(self, autoconnect):
77 if self._autoconnect:
78 self.autoconnect(autoconnect)
79
80 def autoconnect(self, profile_keys):
81 """Automatically connect profiles
82
83 @param profile_keys(iterable): list of profile keys to connect
84 """
85 if not profile_keys:
86 log.warning("No profile given to autoconnect")
87 return
88 self._autoconnect = True
89 self._autoconnect_profiles = []
90 self._do_autoconnect(profile_keys)
91
92 def _do_autoconnect(self, profile_keys):
93 """Connect automatically given profiles
94
95 @param profile_kes(iterable): profiles to connect
96 """
97 assert self._autoconnect
98
99 def authenticate_cb(data, cb_id, profile):
100
101 if C.bool(data.pop("validated", C.BOOL_FALSE)):
102 self._autoconnect_profiles.append(profile)
103 if len(self._autoconnect_profiles) == len(profile_keys):
104 # all the profiles have been validated
105 self.host.plug_profiles(self._autoconnect_profiles)
106 else:
107 # a profile is not validated, we go to manual mode
108 self._autoconnect = False
109 self.host.action_manager(data, callback=authenticate_cb, profile=profile)
110
111 def get_profile_name_cb(profile):
112 if not profile:
113 # FIXME: this method is not handling manual mode correclty anymore
114 # must be thought to be handled asynchronously
115 self._autoconnect = False # manual mode
116 msg = _("Trying to plug an unknown profile key ({})".format(profile_key))
117 log.warning(msg)
118 self.host.show_dialog(_("Profile plugging in error"), msg, "error")
119 else:
120 self.host.action_launch(
121 C.AUTHENTICATE_PROFILE_ID, callback=authenticate_cb, profile=profile
122 )
123
124 def get_profile_name_eb(failure):
125 log.error("Can't retrieve profile name: {}".format(failure))
126
127 for profile_key in profile_keys:
128 self.host.bridge.profile_name_get(
129 profile_key, callback=get_profile_name_cb, errback=get_profile_name_eb
130 )
131
132 def get_param_error(self, __):
133 self.host.show_dialog(_("Error"), _("Can't get profile parameter"), "error")
134
135 ## Helping methods ##
136
137 def _get_error_message(self, reason):
138 """Return an error message corresponding to profile creation error
139
140 @param reason (str): reason as returned by profile_create
141 @return (unicode): human readable error message
142 """
143 if reason == "ConflictError":
144 message = _("A profile with this name already exists")
145 elif reason == "CancelError":
146 message = _("Profile creation cancelled by backend")
147 elif reason == "ValueError":
148 message = _(
149 "You profile name is not valid"
150 ) # TODO: print a more informative message (empty name, name starting with '@')
151 else:
152 message = _("Can't create profile ({})").format(reason)
153 return message
154
155 def _delete_profile(self):
156 """Delete the currently selected profile"""
157 if self.current.profile:
158 self.host.bridge.profile_delete_async(
159 self.current.profile, callback=self.refill_profiles
160 )
161 self.reset_fields()
162
163 ## workflow methods (events occuring during the profiles selection) ##
164
165 # These methods must be called by the frontend at some point
166
167 def _on_connect_profiles(self):
168 """Connect the profiles and start the main widget"""
169 if self._autoconnect:
170 self.host.show_dialog(
171 _("Internal error"),
172 _("You can't connect manually and automatically at the same time"),
173 "error",
174 )
175 return
176 self.update_connection_params()
177 profiles = self.get_profiles()
178 if not profiles:
179 self.host.show_dialog(
180 _("No profile selected"),
181 _("You need to create and select at least one profile before connecting"),
182 "error",
183 )
184 else:
185 # All profiles in the list are already validated, so we can plug them directly
186 self.host.plug_profiles(profiles)
187
188 def get_connection_params(self, profile):
189 """Get login and password and display them
190
191 @param profile: %(doc_profile)s
192 """
193 self.host.bridge.param_get_a_async(
194 "JabberID",
195 "Connection",
196 profile_key=profile,
197 callback=self.set_jid,
198 errback=self.get_param_error,
199 )
200 self.host.bridge.param_get_a_async(
201 "Password",
202 "Connection",
203 profile_key=profile,
204 callback=self.set_password,
205 errback=self.get_param_error,
206 )
207
208 def update_connection_params(self):
209 """Check if connection parameters have changed, and update them if so"""
210 if self.current.profile:
211 login = self.get_jid()
212 password = self.getPassword()
213 if login != self.current.login and self.current.login is not None:
214 self.current.login = login
215 self.host.bridge.param_set(
216 "JabberID", login, "Connection", profile_key=self.current.profile
217 )
218 log.info("login updated for profile [{}]".format(self.current.profile))
219 if password != self.current.password and self.current.password is not None:
220 self.current.password = password
221 self.host.bridge.param_set(
222 "Password", password, "Connection", profile_key=self.current.profile
223 )
224 log.info(
225 "password updated for profile [{}]".format(self.current.profile)
226 )
227
228 ## graphic updates (should probably be overriden in frontends) ##
229
230 def reset_fields(self):
231 """Set profile to None, and reset fields"""
232 self.current.profile = None
233 self.set_jid("")
234 self.set_password("")
235
236 def refill_profiles(self):
237 """Rebuild the list of profiles"""
238 profiles = self.host.bridge.profiles_list_get()
239 profiles.sort()
240 self.set_profiles(profiles)
241
242 ## Method which must be implemented by frontends ##
243
244 # get/set data
245
246 def get_profiles(self):
247 """Return list of selected profiles
248
249 Must be implemented by frontends
250 @return (list): list of profiles
251 """
252 raise NotImplementedError
253
254 def set_profiles(self, profiles):
255 """Update the list of profiles"""
256 raise NotImplementedError
257
258 def get_jid(self):
259 """Get current jid
260
261 Must be implemented by frontends
262 @return (unicode): current jabber id
263 """
264 raise NotImplementedError
265
266 def getPassword(self):
267 """Get current password
268
269 Must be implemented by frontends
270 @return (unicode): current password
271 """
272 raise NotImplementedError
273
274 def set_jid(self, jid_):
275 """Set current jid
276
277 Must be implemented by frontends
278 @param jid_(unicode): jabber id to set
279 """
280 raise NotImplementedError
281
282 def set_password(self, password):
283 """Set current password
284
285 Must be implemented by frontends
286 """
287 raise NotImplementedError
288
289 # dialogs
290
291 # Note: a method which check profiles change must be implemented too