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

Last change on this file since 7509 was 7509, checked in by Juanma, 17 months ago

add vertical orientation

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