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

Last change on this file since 7506 was 7506, checked in by Juanma, 19 months ago

added vertical orientation (experimental)

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