source: n4dgtklogin/trunk/fuentes/python3-n4dgtklogin.install/edupals/ui/n4dgtklogin.py @ 7933

Last change on this file since 7933 was 7933, checked in by Juanma, 16 months ago

add options

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