source: n4dgtklogin/trunk/fuentes/python-n4dgtklogin.install/edupals/ui/n4dgtklogin.py @ 7011

Last change on this file since 7011 was 7011, checked in by Juanma, 2 years ago

show server field when login fails with unknown host

File size: 11.5 KB
Line 
1#!/usr/bin/env python3
2###
3#This class returns a login_grid whith a standarized login form
4###
5import os,sys,socket
6import threading
7import gi
8gi.require_version('Gtk', '3.0')
9gi.require_version('PangoCairo', '1.0')
10import json
11import cairo
12#import commands
13from gi.repository import Gtk, Gdk, GdkPixbuf, GObject, GLib, PangoCairo, Pango
14try:
15        import xmlrpclib as n4d
16except ImportError:
17        raise ImportError("xmlrpc not available. Disabling server queries")
18import time
19import gettext
20
21gettext.textdomain('edupals.ui.common')
22_ = gettext.gettext
23GObject.threads_init()
24
25class N4dGtkLogin(Gtk.Box):
26        __gtype_name__='n4dgtklogin'
27
28        def __init__(self,*args,**kwds):
29                super(Gtk.Box,self).__init__(*args,**kwds)
30                self.sw_n4d=True
31                if hasattr(sys,'last_value'):
32                #If there's any error at this point it only could be an ImportError caused by xmlrpc
33                        self.sw_n4d=False
34                self.css_classes={}
35                self.style_provider=Gtk.CssProvider()
36                Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(),self.style_provider,Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
37                self.default_spacing=12
38                self.username_placeholder=_("Username")
39                self.server_placeholder=_("Server IP (Default value : server)")
40                self.login_banner_default="llx-avatar"
41                self.info_banner=None
42                self.allowed_groups=[]
43                #internal boxes
44                self.form_box=Gtk.Box(spacing=0,orientation=Gtk.Orientation.VERTICAL)
45                self.info_box=Gtk.Box(spacing=self.default_spacing,orientation=Gtk.Orientation.VERTICAL)
46                self.main_grid=Gtk.Grid()
47                self.render_form()
48        #def __init__
49
50        def set_allowed_groups(self,groups):
51                self.allowed_groups=groups
52        #def set_allowed_groups
53
54        def set_mw_proportion_ratio(self,left_panel,right_panel):
55                for child in self.main_grid.get_children():
56                        self.main_grid.remove(child)
57                self.main_grid.attach(self.info_box,0,0,left_panel,1)
58                self.main_grid.attach(self.form_box,0+left_panel,0,right_panel,1)
59        #def set_mw_proportion_ratio
60       
61        def set_mw_background(self,image=None,cover=False,from_color='#ffffff',to_color='silver',gradient='linear'):
62                mw_background=self._set_background(image,cover,from_color,to_color,gradient)
63                self.css_classes['#mw']='{'+mw_background+';;}'
64                self._set_css()
65        #def set_mw_background
66
67        def set_login_background(self,image=None,cover=False,from_color='#ffffff',to_color='@silver',gradient='linear'):
68                form_background=self._set_background(image,cover,from_color,to_color,gradient)
69                self.css_classes['#main']='{'+form_background+';;}'
70                self._set_css()
71        #def set_login_background
72
73        def set_info_background(self,image=None,cover=False,from_color='#ffffff',to_color='@silver',gradient='linear'):
74                info_background=self._set_background(image,cover,from_color,to_color,gradient)
75                self.css_classes['#info']='{'+info_background+';;}'
76                self._set_css()
77        #def set_info_background
78
79        def _set_css(self):
80                css=''
81                for css_class,style in self.css_classes.items():
82                        css=css+css_class+' '+style
83                css_style=eval('b"""'+css+'"""')
84                self.style_provider.load_from_data(css_style)
85        #def _set_css
86
87        def _set_background(self,image=None,cover=False,from_color='#ffffff',to_color='silver',gradient='linear'):
88                bg=''
89                if image and os.path.isfile(image):
90                        if cover:
91                                bg='background-image:url("'+image+'"); background-repeat:no-repeat; background-size:100% 100%'
92                        else:
93                                bg='background-image:url("'+image+'"); background-repeat:no-repeat;'
94                elif image:
95                        #try to locate the image in the default theme
96                        icon_theme=Gtk.IconTheme.get_default()
97                        icon_sizes=icon_theme.get_icon_sizes(image)
98                        if icon_sizes:
99                                max_size=max(icon_sizes)
100                                icon=icon_theme.lookup_icon(image,max_size,0)
101                                icon_path=icon.get_filename()
102                                bg='background-image:url("'+icon_path+'"); background-repeat:no-repeat; background-size:100% 100%'
103
104                else:
105                        if gradient=='linear':
106                                bg='background-image:-gtk-gradient (linear, left top, left bottom, from ('+from_color+'),  to ('+to_color+'))'
107                        elif gradient=='radial':
108                                bg='background-image:-gtk-gradient (radial, center center,0,center center,1, from ('+from_color+'),  to ('+to_color+'))'
109                return bg
110        #def _set_background
111
112        def set_default_username(self,username):
113                self.username_placeholder=username
114                self._set_text_for_entry(self.txt_username,username)
115        #def set_default_username
116
117        def set_default_server(self,server):
118                self.server_placeholder=server
119                if self.txt_server:
120                        self._set_text_for_entry(self.txt_server,server)
121        #def set_default_server
122       
123        def _set_text_for_entry(self,widget,text):
124                widget.set_placeholder_text(text)
125        #def _set_text_for_entry
126
127        def _lookup_user_face(self):
128                sw_ok=False
129                if os.path.isfile(os.path.expanduser('~/.face')):
130                        sw_ok=True
131                        self.set_login_banner(os.path.expanduser('~/.face'))
132                return sw_ok
133        #def _lookup_user_face
134
135        def set_login_banner(self,banner):
136                self.login_banner.set_from_pixbuf(self._get_image(banner))
137        #def set_banner
138
139        def set_info_banner(self,banner,x=72,y=72):
140                self.info_banner.set_from_pixbuf(self._get_image(banner))
141        #def set_info_banner
142
143        def _get_image(self,image,x=72,y=72):
144                icon_theme=Gtk.IconTheme.get_default()
145                if icon_theme.has_icon(image):
146                        pixbuf=icon_theme.load_icon(image,x,0)
147                else:
148                        if os.path.isfile(image):
149                                pixbuf=GdkPixbuf.Pixbuf.new_from_file_at_scale(image,x,y,True)
150                return pixbuf
151        #def _get_image
152       
153        def set_info_text(self,title,subtitle,text):
154                sw_ok=True
155                info_msg=''
156                self.lbl_info_msg.set_width_chars(25)
157                self.lbl_info_msg.set_max_width_chars(25)
158                try:
159                        msg="<b><big>"+title+"</big></b>"
160                        info_msg=msg+'\n'+subtitle+'\n\n'+text
161                except Exception as e:
162                        sw_ok=False
163                        print(e)
164                self.lbl_info_msg.set_markup(info_msg)
165                self.lbl_info_msg.show()
166                return sw_ok
167        #def set_info_text
168
169        def hide_server_entry(self):
170                self.txt_server.props.no_show_all=True
171                self.txt_server.hide()
172        #def hide_server_entry
173
174        def hide_info_box(self):
175                self.info_box.props.no_show_all=True
176                self.info_box.hide()
177        #def hide_info_box
178
179        def get_action_area(self):
180                return self.info_box
181        #def get_action_area
182
183        def render_form(self):
184                self.main_grid.set_hexpand(True)
185                self.main_grid.set_vexpand(True)
186                self.main_grid.set_column_homogeneous(True)
187                self.main_grid.set_row_homogeneous(True)
188                self._render_login_form()
189                self._render_info_form()
190                self.main_grid.attach(self.info_box,1,1,2,1)
191                self.main_grid.attach(self.form_box,3,1,1,1)
192                self.pack_start(self.main_grid,True,True,0)
193                self.set_name("mw")
194                self.form_box.set_name("main")
195        #def render_form
196
197        def _render_login_form(self):
198                self.txt_username=Gtk.Entry()
199                self.login_banner=Gtk.Image()
200                if not self._lookup_user_face():
201                        self.set_login_banner(self.login_banner_default)
202                self.login_banner.set_margin_bottom(self.default_spacing)
203                self.set_default_username(self.username_placeholder)
204                self.txt_username.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,"emblem-personal")
205                self.txt_password=Gtk.Entry()
206                self.txt_password.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,"badge-small")
207                self._set_text_for_entry(self.txt_password,_("Password"))
208                self.txt_server=Gtk.Entry()
209                self.set_default_server(self.server_placeholder)
210                self.txt_server.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,"server")
211                self.btn_sign=Gtk.Button(stock=Gtk.STOCK_OK)
212                self.btn_sign.connect('clicked',self._validate)
213                self.frame=Gtk.Frame()
214               
215                self.frame.set_shadow_type(Gtk.ShadowType.OUT)   
216                login_grid=Gtk.Grid()
217                login_grid.set_margin_left(self.default_spacing)
218                login_grid.set_margin_right(self.default_spacing)
219                login_grid.set_margin_top(self.default_spacing)
220                login_grid.set_margin_bottom(self.default_spacing)
221                self.spinner=Gtk.Spinner()
222                color=Gdk.Color(0,0,1)
223                self.spinner.modify_bg(Gtk.StateType.NORMAL,color)
224                login_grid.attach(self.spinner,0,1,1,5)
225                login_grid.attach(self.login_banner,0,0,1,1)
226                login_grid.attach(self.txt_username,0,1,1,1)
227                self._set_widget_default_props(self.txt_username,_("Username"))
228                self.txt_username.connect('activate',self._validate)
229                login_grid.attach(self.txt_password,0,2,1,1)
230                self._set_widget_default_props(self.txt_password,_("Password"))
231                self.txt_password.set_visibility(False)
232                self.txt_password.props.caps_lock_warning=True
233                self.txt_password.connect('activate',self._validate)
234                login_grid.attach(self.txt_server,0,3,1,1)
235                self._set_widget_default_props(self.txt_server,_("Master Server IP"))
236                self.txt_server.connect('activate',self._validate)
237                self.btn_sign.set_margin_top(self.default_spacing)
238                login_grid.attach(self.btn_sign,0,4,1,1)
239                self.sta_info=Gtk.InfoBar()
240                self.sta_info.set_show_close_button(True)
241                self.sta_info.set_message_type(Gtk.MessageType.ERROR)
242                self.lbl_error=Gtk.Label(_("Login failed"))
243                self.sta_info.get_action_area().add(self.lbl_error)
244                self.sta_info.set_visible(False)
245                self.sta_info.set_no_show_all(True)
246                self.sta_info.connect('response',self._status_info_hide)
247                self.sta_info.set_valign(True)
248                login_grid.props.valign=Gtk.Align.CENTER
249                login_grid.props.halign=Gtk.Align.CENTER
250                self.form_box.pack_start(self.sta_info,False,True,0)
251                self.form_box.pack_start(login_grid,True,True,0)
252        #def _render_login_form
253
254        def _render_info_form(self):
255                self.info_box.set_homogeneous(False)
256                self.info_banner=Gtk.Image()
257                info_detail_box=Gtk.Box(spacing=self.default_spacing,orientation=Gtk.Orientation.VERTICAL)
258                info_detail_box.pack_start(self.info_banner,False,False,0)
259                self.lbl_info_msg=Gtk.Label()
260                self.lbl_info_msg.set_use_markup(True)
261                self.lbl_info_msg.set_line_wrap(True)
262                info_detail_box.pack_start(self.lbl_info_msg,True,True,0)
263                self.css_classes['#label']='{background-color:rgba(200,200,200,0.8);;}'
264                self.lbl_info_msg.set_name("label")
265                self.lbl_info_msg.set_no_show_all(True)
266                self.lbl_info_msg.hide()
267                self.info_box.set_name("info")
268                info_detail_box.props.valign=Gtk.Align.CENTER
269                info_detail_box.props.halign=Gtk.Align.CENTER
270                self.info_box.pack_start(info_detail_box,True,True,0)
271                self._set_css()
272        #def _render_info_form
273
274        def _set_widget_default_props(self,widget,tooltip=None):
275                widget.set_valign(True)
276                widget.set_halign(True)
277                widget.set_tooltip_text(tooltip)
278        #def _set_widget_default_props
279
280        def _status_info_hide(self,widget,data):
281                self.sta_info.hide()
282                self.frame.set_sensitive(True)
283        #def _info_hide
284
285        def _validate(self,widget=None):
286                user=self.txt_username.get_text()
287                pwd=self.txt_password.get_text()
288                server=self.txt_server.get_text()
289                if not server:
290                        server='server'
291                        try:
292                                socket.gethostbyname(server)
293                        except:
294                                server='localhost'
295                self.spinner.start()
296                self.frame.set_sensitive(False)
297                th=threading.Thread(target=self._t_validate,args=[user,pwd,server])
298                th.start()
299        #def _validate
300
301        def _t_validate(self,user,pwd,server):
302                ret=[False]
303                self.lbl_error.set_text(_("Login failed"))
304                if self.sw_n4d:
305                        try:
306                                self.n4dclient=self._n4d_connect(server)
307                                ret=self.n4dclient.validate_user(user,pwd)
308                        except socket.error as e:
309                                self.lbl_error.set_text(_("Unknown host %s"%server))
310                                ret=[False,str(e)]
311
312                self.spinner.stop()
313                if not ret[0]:
314                        self.sta_info.show()
315                        self.lbl_error.show()
316                        #show server entry if we can't connect to n4d in "server"
317                        self.txt_server.props.no_show_all=False
318                        self.txt_server.show()
319                elif self.allowed_groups and not set(self.allowed_groups).intersection(ret[1]):
320                        #Check user groups
321                        self.lbl_error.set_text(_("User not allowed"))
322                        self.sta_info.show()
323                        self.lbl_error.show()
324                else:
325                        GLib.idle_add(self.after_validate,user,pwd,server)
326                #local validation
327        #def _t_validate
328
329        def after_validation_goto(self,func,data=None):
330                self.after_validate=func
331        #def after_validation_func
332
333        def _n4d_connect(self,server):
334                try:
335                        socket.gethostbyname(server)
336                except:
337                        #It could be an ip
338                        try:
339                                socket.inet_aton(server)
340                        except Exception as e:
341                                print(e)
342                                raise
343                c = n4d.ServerProxy("https://"+server+":9779")
344                return c
345        #def _n4d_connect
Note: See TracBrowser for help on using the repository browser.