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