comparison sat_frontends/quick_frontend/quick_profile_manager.py @ 2562:26edcf3a30eb

core, setup: huge cleaning: - moved directories from src and frontends/src to sat and sat_frontends, which is the recommanded naming convention - move twisted directory to root - removed all hacks from setup.py, and added missing dependencies, it is now clean - use https URL for website in setup.py - removed "Environment :: X11 Applications :: GTK", as wix is deprecated and removed - renamed sat.sh to sat and fixed its installation - added python_requires to specify Python version needed - replaced glib2reactor which use deprecated code by gtk3reactor sat can now be installed directly from virtualenv without using --system-site-packages anymore \o/
author Goffi <goffi@goffi.org>
date Mon, 02 Apr 2018 19:44:50 +0200
parents frontends/src/quick_frontend/quick_profile_manager.py@0046283a285d
children 56f94936df1e
comparison
equal deleted inserted replaced
2561:bd30dc3ffe5a 2562:26edcf3a30eb
1 #!/usr/bin/env python2
2 # -*- coding: utf-8 -*-
3
4 # helper class for making a SAT frontend
5 # Copyright (C) 2009-2018 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 sat.core.i18n import _
21 from sat.core import log as logging
22 log = logging.getLogger(__name__)
23 from sat_frontends.primitivus.constants import Const as C
24
25
26 class ProfileRecord(object):
27 """Class which manage data for one profile"""
28
29 def __init__(self, profile=None, login=None, password=None):
30 self._profile = profile
31 self._login = login
32 self._password = password
33
34 @property
35 def profile(self):
36 return self._profile
37
38 @profile.setter
39 def profile(self, value):
40 self._profile = value
41 # if we change the profile,
42 # we must have no login/password until backend give them
43 self._login = self._password = None
44
45 @property
46 def login(self):
47 return self._login
48
49 @login.setter
50 def login(self, value):
51 self._login = value
52
53 @property
54 def password(self):
55 return self._password
56
57 @password.setter
58 def password(self, value):
59 self._password = value
60
61
62 class QuickProfileManager(object):
63 """Class with manage profiles creation/deletion/connection"""
64
65 def __init__(self, host, autoconnect=None):
66 """Create the manager
67
68 @param host: %(doc_host)s
69 @param autoconnect(iterable): list of profiles to connect automatically
70 """
71 self.host = host
72 self._autoconnect = bool(autoconnect)
73 self.current = ProfileRecord()
74
75 def go(self, autoconnect):
76 if self._autoconnect:
77 self.autoconnect(autoconnect)
78
79 def autoconnect(self, profile_keys):
80 """Automatically connect profiles
81
82 @param profile_keys(iterable): list of profile keys to connect
83 """
84 if not profile_keys:
85 log.warning("No profile given to autoconnect")
86 return
87 self._autoconnect = True
88 self._autoconnect_profiles=[]
89 self._do_autoconnect(profile_keys)
90
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.actionManager(data, callback=authenticate_cb, profile=profile)
110
111 def getProfileNameCb(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.showDialog(_("Profile plugging in error"), msg, 'error')
119 else:
120 self.host.launchAction(C.AUTHENTICATE_PROFILE_ID, callback=authenticate_cb, profile=profile)
121
122 def getProfileNameEb(failure):
123 log.error(u"Can't retrieve profile name: {}".format(failure))
124
125 for profile_key in profile_keys:
126 self.host.bridge.profileNameGet(profile_key, callback=getProfileNameCb, errback=getProfileNameEb)
127
128
129 def getParamError(self, dummy):
130 self.host.showDialog(_(u"Error"), _("Can't get profile parameter"), 'error')
131
132 ## Helping methods ##
133
134 def _getErrorMessage(self, reason):
135 """Return an error message corresponding to profile creation error
136
137 @param reason (str): reason as returned by profileCreate
138 @return (unicode): human readable error message
139 """
140 if reason == "ConflictError":
141 message = _("A profile with this name already exists")
142 elif reason == "CancelError":
143 message = _("Profile creation cancelled by backend")
144 elif reason == "ValueError":
145 message = _("You profile name is not valid") # TODO: print a more informative message (empty name, name starting with '@')
146 else:
147 message = _("Can't create profile ({})").format(reason)
148 return message
149
150 def _deleteProfile(self):
151 """Delete the currently selected profile"""
152 if self.current.profile:
153 self.host.bridge.asyncDeleteProfile(self.current.profile, callback=self.refillProfiles)
154 self.resetFields()
155
156 ## workflow methods (events occuring during the profiles selection) ##
157
158 # These methods must be called by the frontend at some point
159
160 def _onConnectProfiles(self):
161 """Connect the profiles and start the main widget"""
162 if self._autoconnect:
163 self.host.showDialog(_('Internal error'), _("You can't connect manually and automatically at the same time"), 'error')
164 return
165 self.updateConnectionParams()
166 profiles = self.getProfiles()
167 if not profiles:
168 self.host.showDialog(_('No profile selected'), _('You need to create and select at least one profile before connecting'), 'error')
169 else:
170 # All profiles in the list are already validated, so we can plug them directly
171 self.host.plug_profiles(profiles)
172
173 def getConnectionParams(self, profile):
174 """Get login and password and display them
175
176 @param profile: %(doc_profile)s
177 """
178 self.host.bridge.asyncGetParamA("JabberID", "Connection", profile_key=profile, callback=self.setJID, errback=self.getParamError)
179 self.host.bridge.asyncGetParamA("Password", "Connection", profile_key=profile, callback=self.setPassword, errback=self.getParamError)
180
181 def updateConnectionParams(self):
182 """Check if connection parameters have changed, and update them if so"""
183 if self.current.profile:
184 login = self.getJID()
185 password = self.getPassword()
186 if login != self.current.login and self.current.login is not None:
187 self.current.login = login
188 self.host.bridge.setParam("JabberID", login, "Connection", profile_key=self.current.profile)
189 log.info(u"login updated for profile [{}]".format(self.current.profile))
190 if password != self.current.password and self.current.password is not None:
191 self.current.password = password
192 self.host.bridge.setParam("Password", password, "Connection", profile_key=self.current.profile)
193 log.info(u"password updated for profile [{}]".format(self.current.profile))
194
195 ## graphic updates (should probably be overriden in frontends) ##
196
197 def resetFields(self):
198 """Set profile to None, and reset fields"""
199 self.current.profile=None
200 self.setJID("")
201 self.setPassword("")
202
203 def refillProfiles(self):
204 """Rebuild the list of profiles"""
205 profiles = self.host.bridge.profilesListGet()
206 profiles.sort()
207 self.setProfiles(profiles)
208
209 ## Method which must be implemented by frontends ##
210
211 # get/set data
212
213 def getProfiles(self):
214 """Return list of selected profiles
215
216 Must be implemented by frontends
217 @return (list): list of profiles
218 """
219 raise NotImplementedError
220
221 def setProfiles(self, profiles):
222 """Update the list of profiles"""
223 raise NotImplementedError
224
225
226 def getJID(self):
227 """Get current jid
228
229 Must be implemented by frontends
230 @return (unicode): current jabber id
231 """
232 raise NotImplementedError
233
234 def getPassword(self):
235 """Get current password
236
237 Must be implemented by frontends
238 @return (unicode): current password
239 """
240 raise NotImplementedError
241
242 def setJID(self, jid_):
243 """Set current jid
244
245 Must be implemented by frontends
246 @param jid_(unicode): jabber id to set
247 """
248 raise NotImplementedError
249
250 def setPassword(self, password):
251 """Set current password
252
253 Must be implemented by frontends
254 """
255 raise NotImplementedError
256
257 # dialogs
258
259 # Note: a method which check profiles change must be implemented too