2
# -*- coding: UTF-8 -*-
3
###########################################################################
4
# userconfig.py - description #
5
# ------------------------------ #
6
# begin : Wed Apr 30 2003 #
7
# copyright : (C) 2003-2006 by Simon Edwards #
8
# email : simon@simonzone.com #
10
###########################################################################
12
# This program is free software; you can redistribute it and/or modify #
13
# it under the terms of the GNU General Public License as published by #
14
# the Free Software Foundation; either version 2 of the License, or #
15
# (at your option) any later version. #
17
###########################################################################
29
programname = "userconfig"
31
# Are we running as a separate standalone application or in KControl?
32
standalone = __name__=='__main__'
34
# Running as the root user or not?
35
isroot = os.getuid()==0
38
###########################################################################
39
def SptimeToQDate(sptime):
42
return t.addDays(sptime).date()
44
###########################################################################
45
def QDateToSptime(qdate):
48
return x.daysTo(QDateTime(qdate))
50
###########################################################################
51
# Try translating this code to C++. I dare ya!
53
programbase = KDialogBase
55
programbase = KCModule
57
class UserConfigApp(programbase):
58
def __init__(self,parent=None,name=None):
59
global standalone,isroot
60
KGlobal.locale().insertCatalogue("guidance")
63
KDialogBase.__init__(self,KJanusWidget.Tabbed,i18n("User Accounts and Groups"),
64
KDialogBase.User1|KDialogBase.Close, KDialogBase.Close)
65
self.setButtonText(KDialogBase.User1,i18n("About"))
67
KCModule.__init__(self,parent,name)
69
self.aboutdata = MakeAboutData()
71
toplayout = QVBoxLayout( self, 0, KDialog.spacingHint() )
72
tabcontrol = QTabWidget(self)
73
toplayout.addWidget(tabcontrol)
74
toplayout.setStretchFactor(tabcontrol,1)
76
# Create a configuration object.
77
self.config = KConfig("userconfigrc")
79
KGlobal.iconLoader().addAppDir("guidance")
81
self.usersToListItems = None
82
self.selecteduserid = None
83
self.selectedgroupid = None
84
self.showsystemaccounts = False
85
self.showsystemgroups = False
87
self.updatingGUI = True
90
self.aboutus = KAboutApplication(self)
94
usershbox = self.addHBoxPage("Users")
95
vbox = QVBox(usershbox)
97
vbox = QVBox(tabcontrol)
98
vbox.setMargin(KDialog.marginHint())
100
vbox.setSpacing(KDialog.spacingHint())
103
hb.setSpacing(KDialog.spacingHint())
104
vbox.setStretchFactor(hb,0)
107
label.setPixmap(UserIcon("hi32-user"))
108
hb.setStretchFactor(label,0)
110
label = QLabel(i18n("User Accounts:"),hb)
111
hb.setStretchFactor(label,1)
113
self.userlist = KListView(vbox)
114
self.userlist.addColumn(i18n("Login Name"))
115
self.userlist.addColumn(i18n("Real Name"))
116
self.userlist.addColumn(i18n("UID"))
117
self.userlist.setAllColumnsShowFocus(True)
118
self.userlist.setSelectionMode(QListView.Single)
120
self.connect(self.userlist, SIGNAL("selectionChanged(QListViewItem *)"), self.slotListClicked)
122
self.connect(self.userlist, SIGNAL("doubleClicked(QListViewItem *)"), self.slotModifyClicked)
123
self.connect(self.userlist, SIGNAL("contextMenu(KListView*,QListViewItem*,const QPoint&)"), self.slotUserContext)
125
self.showspecialcheckbox = QCheckBox(i18n("Show system accounts"),vbox)
126
vbox.setStretchFactor(self.showspecialcheckbox,0)
127
self.connect(self.showspecialcheckbox,SIGNAL("toggled(bool)"), self.slotShowSystemToggled)
130
hbox.setSpacing(KDialog.spacingHint())
132
vbox.setStretchFactor(hbox,0)
134
self.modifybutton = KPushButton(i18n("Modify..."),hbox)
135
hbox.setStretchFactor(self.modifybutton,1)
136
self.connect(self.modifybutton,SIGNAL("clicked()"),self.slotModifyClicked)
138
self.newbutton = KPushButton(i18n("New..."),hbox)
139
hbox.setStretchFactor(self.newbutton,1)
140
self.connect(self.newbutton,SIGNAL("clicked()"),self.slotNewClicked)
142
self.deletebutton = KPushButton(i18n("Delete..."),hbox)
143
hbox.setStretchFactor(self.deletebutton,1)
144
self.connect(self.deletebutton,SIGNAL("clicked()"),self.slotDeleteClicked)
146
detailsbox = QVGroupBox(i18n("Details"),vbox)
147
userinfovbox = QWidget(detailsbox)
149
infogrid = QGridLayout(userinfovbox,3,4)
150
infogrid.setSpacing(KDialog.spacingHint())
152
label = QLabel(i18n("Login Name:"),userinfovbox)
153
infogrid.addWidget(label,0,0)
154
self.loginnamelabel = KLineEdit("",userinfovbox)
155
self.loginnamelabel.setReadOnly(True)
156
infogrid.addWidget(self.loginnamelabel,0,1)
158
label = QLabel(i18n("Real Name:"),userinfovbox)
159
infogrid.addWidget(label,0,2)
160
self.realnamelabel = KLineEdit("",userinfovbox)
161
self.realnamelabel.setReadOnly(True)
162
infogrid.addWidget(self.realnamelabel,0,3)
164
label = QLabel(i18n("UID:"),userinfovbox)
165
infogrid.addWidget(label,1,0)
166
self.uidlabel = KLineEdit("",userinfovbox)
167
self.uidlabel.setReadOnly(True)
168
infogrid.addWidget(self.uidlabel,1,1)
170
label = QLabel(i18n("Status:"),userinfovbox)
171
infogrid.addWidget(label,1,2)
172
self.statuslabel = KLineEdit("",userinfovbox)
173
self.statuslabel.setReadOnly(True)
174
infogrid.addWidget(self.statuslabel,1,3)
176
label = QLabel(i18n("Primary Group:"),userinfovbox)
177
infogrid.addWidget(label,2,0)
178
self.primarygrouplabel = KLineEdit("",userinfovbox)
179
self.primarygrouplabel.setReadOnly(True)
180
infogrid.addWidget(self.primarygrouplabel,2,1)
182
label = QLabel(i18n("Secondary Groups:"),userinfovbox)
183
infogrid.addWidget(label,2,2)
184
self.secondarygrouplabel = KLineEdit("",userinfovbox)
185
self.secondarygrouplabel.setReadOnly(True)
186
infogrid.addWidget(self.secondarygrouplabel,2,3)
189
tabcontrol.addTab(vbox,i18n("Users"))
193
groupsvbox = self.addVBoxPage(i18n("Groups"))
194
hb = QHBox(groupsvbox)
196
groupsvbox = QVBox(tabcontrol)
197
groupsvbox.setMargin(KDialog.marginHint())
198
hb = QHBox(groupsvbox)
200
topframe = QFrame(groupsvbox)
201
groupsvbox.setSpacing(KDialog.spacingHint())
202
hb.setSpacing(KDialog.spacingHint())
203
groupsvbox.setStretchFactor(hb,0)
206
label.setPixmap(UserIcon("hi32-group"))
207
hb.setStretchFactor(label,0)
209
label = QLabel(i18n("Groups:"),hb)
210
hb.setStretchFactor(label,1)
212
groupsplitter = QSplitter(Qt.Vertical,groupsvbox)
214
self.grouplist = KListView(groupsplitter)
215
self.grouplist.addColumn(i18n("Group Name"))
216
self.grouplist.addColumn(i18n("GID"))
217
self.grouplist.setAllColumnsShowFocus(True)
218
self.connect(self.grouplist, SIGNAL("selectionChanged(QListViewItem *)"), self.slotGroupListClicked)
221
self.connect(self.grouplist, SIGNAL("doubleClicked(QListViewItem *)"), self.slotModifyGroupClicked)
222
self.connect(self.grouplist, SIGNAL("contextMenu(KListView*,QListViewItem*,const QPoint&)"),
223
self.slotGroupContext)
225
groupbottomvbox = QVBox(groupsplitter)
226
groupbottomvbox.setSizePolicy(QSizePolicy.Expanding,QSizePolicy.Expanding)
228
self.showspecialgroupscheckbox = QCheckBox(i18n("Show system groups"),groupbottomvbox)
229
vbox.setStretchFactor(self.showspecialgroupscheckbox,0)
230
self.connect(self.showspecialgroupscheckbox,SIGNAL("toggled(bool)"), self.slotShowSystemGroupsToggled)
232
hbox = QHBox(groupbottomvbox)
233
hbox.setSpacing(KDialog.spacingHint())
235
groupsvbox.setStretchFactor(hbox,0)
237
self.modifygroupbutton = KPushButton(i18n("Modify..."),hbox)
238
hbox.setStretchFactor(self.modifygroupbutton,1)
239
self.connect(self.modifygroupbutton,SIGNAL("clicked()"),self.slotModifyGroupClicked)
241
self.newgroupbutton = KPushButton(i18n("New..."),hbox)
242
hbox.setStretchFactor(self.newgroupbutton,1)
243
self.connect(self.newgroupbutton,SIGNAL("clicked()"),self.slotNewGroupClicked)
245
self.deletegroupbutton = KPushButton(i18n("Delete..."),hbox)
246
hbox.setStretchFactor(self.deletegroupbutton,1)
247
self.connect(self.deletegroupbutton,SIGNAL("clicked()"),self.slotDeleteGroupClicked)
250
disablebuttons = ( self.modifybutton, self.modifygroupbutton, self.deletebutton, self.deletegroupbutton,
251
self.newbutton, self.newgroupbutton)
252
for widget in disablebuttons:
253
widget.setDisabled(True)
255
label = QLabel(i18n("Group Members:"),groupbottomvbox)
256
groupsvbox.setStretchFactor(label,0)
258
self.groupmemberlist = KListView(groupbottomvbox)
259
self.groupmemberlist.addColumn(i18n("Login Name"))
260
self.groupmemberlist.addColumn(i18n("Real Name"))
261
self.groupmemberlist.addColumn(i18n("UID"))
262
self.groupmemberlist.setAllColumnsShowFocus(True)
265
tabcontrol.addTab(groupsvbox,i18n("Groups"))
267
self.admincontext = unixauthdb.getContext(isroot)
269
self.updatingGUI = True
271
self.showspecialcheckbox.setChecked(self.showsystemaccounts)
272
self.showspecialgroupscheckbox.setChecked(self.showsystemgroups)
274
self.__updateUserList()
275
self.__updateGroupList()
276
self.updatingGUI = False
278
self.usereditdialog = UserEditDialog(None,self.admincontext)
279
self.userdeletedialog = UserDeleteDialog(None,self.admincontext)
280
self.groupeditdialog = GroupEditDialog(None,self.admincontext)
282
#######################################################################
286
self.updatingGUI = True
287
self.__updateUserList()
288
self.__updateGroupList()
289
self.updatingGUI = False
290
programbase.exec_loop(self)
293
#######################################################################
297
#######################################################################
298
def slotUserContext(self,l,v,p):
299
cmenu = KPopupMenu(self,"MyActions")
300
cmenu.insertItem(i18n("Modify..."), self.slotModifyClicked, 0, 0)
301
cmenu.insertItem(i18n("Delete..."), self.slotDeleteClicked, 0, 1)
303
cmenu.setItemEnabled(0,False)
304
cmenu.setItemEnabled(1,False)
308
#######################################################################
309
def slotGroupContext(self,l,v,p):
310
cmenu = KPopupMenu(self,"MyActions")
311
cmenu.insertItem(i18n("Modify..."), self.slotModifyGroupClicked, 0, 0)
312
cmenu.insertItem(i18n("Delete..."), self.slotDeleteGroupClicked, 0, 1)
314
cmenu.setItemEnabled(0,False)
315
cmenu.setItemEnabled(1,False)
318
#######################################################################
321
size_hint = programbase.sizeHint(self)
322
# Just make the dialog a little shorter by default.
323
size_hint.setHeight(size_hint.height()-200)
326
#######################################################################
327
def slotCloseButton(self):
330
#######################################################################
331
def slotListClicked(self,item):
332
if self.updatingGUI==False:
333
for userid in self.useridsToListItems:
334
if self.useridsToListItems[userid]==item:
335
self.updatingGUI = True
336
self.__selectUser(userid)
337
self.updatingGUI = False
340
#######################################################################
341
def slotShowSystemToggled(self,on):
342
self.showsystemaccounts = on
343
if self.updatingGUI==False:
344
self.updatingGUI = True
345
self.__updateUserList()
346
self.updatingGUI = False
348
#######################################################################
349
def slotModifyClicked(self):
350
self.usereditdialog.showEditUser(self.selecteduserid)
351
self.updatingGUI = True
352
self.__updateUser(self.selecteduserid)
353
self.__selectUser(self.selecteduserid)
354
self.updatingGUI = False
356
#######################################################################
357
def slotNewClicked(self):
358
newuid = self.usereditdialog.showNewUser()
360
self.updatingGUI = True
361
self.__updateUserList()
362
self.__selectUser(newuid)
363
self.updatingGUI = False
365
#######################################################################
366
def slotDeleteClicked(self):
367
if self.userdeletedialog.deleteUser(self.selecteduserid):
368
self.updatingGUI = True
369
self.selecteduserid = None
370
self.__updateUserList()
371
self.updatingGUI = False
373
#######################################################################
374
def slotGroupListClicked(self,item):
375
if self.updatingGUI==False:
376
for groupid in self.groupidsToListItems:
377
if groupid and self.groupidsToListItems[groupid]==item:
378
self.updatingGUI = True
379
self.__selectGroup(groupid)
380
self.updatingGUI = False
383
#######################################################################
384
def slotShowSystemGroupsToggled(self,on):
385
self.showsystemgroups = on
386
if self.updatingGUI==False:
387
self.updatingGUI = True
388
self.__updateGroupList()
389
self.updatingGUI = False
391
#######################################################################
392
def slotModifyGroupClicked(self):
393
if self.selectedgroupid!=None:
394
if self.groupeditdialog.showEditGroup(self.selectedgroupid):
395
self.__selectGroup(self.selectedgroupid)
396
self.updatingGUI = True
397
self.__updateUser(self.selecteduserid)
398
self.__selectUser(self.selecteduserid)
399
self.updatingGUI = False
401
#######################################################################
402
def slotNewGroupClicked(self):
403
newgroupid = self.groupeditdialog.showNewGroup()
405
self.updatingGUI = True
406
self.__updateGroupList()
407
self.__updateGroupList()
408
self.__selectGroup(newgroupid)
409
self.__updateUser(self.selecteduserid)
410
self.__selectUser(self.selecteduserid)
411
self.updatingGUI = False
413
#######################################################################
414
def slotDeleteGroupClicked(self):
415
if self.selectedgroupid!=None:
416
groupobj = self.admincontext.lookupGID(self.selectedgroupid)
417
groupname = groupobj.getGroupname()
418
gid = groupobj.getGID()
419
nummembers = len(groupobj.getUsers())
421
message = i18n("Are you sure you want to delete group '%1' (%2)?\nIt currently has %3 members.").arg(groupname).arg(gid).arg(nummembers)
422
if KMessageBox.warningYesNo(self,message,i18n("Delete Group?"))==KMessageBox.Yes:
423
self.admincontext.removeGroup(groupobj)
424
self.admincontext.save()
425
self.updatingGUI = True
426
self.__updateGroupList()
427
self.__updateUser(self.selecteduserid)
428
self.__selectUser(self.selecteduserid)
429
self.updatingGUI = False
431
#######################################################################
432
def __updateUserList(self):
433
self.userlist.clear()
434
self.useridsToListItems = {}
435
firstselecteduserid = None
437
users = self.admincontext.getUsers()
439
for userobj in users:
440
uid = userobj.getUID()
441
if self.showsystemaccounts or not userobj.isSystemUser():
442
lvi = KListViewItem(self.userlist,userobj.getUsername(),userobj.getRealName(),unicode(uid))
443
if userobj.isLocked():
444
lvi.setPixmap(0,UserIcon("hi16-encrypted"))
445
self.useridsToListItems[uid] = lvi
446
if self.selecteduserid==uid:
447
firstselecteduserid = uid
448
elif firstselecteduserid==None:
449
firstselecteduserid = uid
450
self.selecteduserid = firstselecteduserid
451
self.__selectUser(self.selecteduserid)
452
self.userlist.ensureItemVisible(self.userlist.currentItem())
454
#######################################################################
455
def __updateUser(self,userid):
456
lvi = self.useridsToListItems[userid]
457
userobj = self.admincontext.lookupUID(userid)
458
lvi.setText(0,userobj.getUsername())
459
lvi.setText(1,userobj.getRealName())
460
lvi.setText(2,unicode(userobj.getUID()))
461
if userobj.isLocked():
462
lvi.setPixmap(0,UserIcon("hi16-encrypted"))
464
lvi.setPixmap(0,QPixmap())
466
#######################################################################
467
def __selectUser(self,userid):
468
self.selecteduserid = userid
469
# Only go on if there are actual users.
470
if len(self.useridsToListItems)>0:
471
lvi = self.useridsToListItems[userid]
472
self.userlist.setSelected(lvi,True)
473
self.userlist.setCurrentItem(lvi)
475
userobj = self.admincontext.lookupUID(userid)
477
username = userobj.getUsername()
478
self.loginnamelabel.setText(username)
479
self.realnamelabel.setText(userobj.getRealName())
480
self.uidlabel.setText(unicode(userid))
481
if userobj.isLocked():
482
self.statuslabel.setText(i18n("Disabled"))
484
self.statuslabel.setText(i18n("Enabled"))
487
primarygroupobj = userobj.getPrimaryGroup()
488
primarygroupname = primarygroupobj.getGroupname()
489
self.primarygrouplabel.setText(primarygroupname)
492
secondarygroups = [g.getGroupname() for g in userobj.getGroups() if g is not userobj.getPrimaryGroup()]
493
self.secondarygrouplabel.setText(unicode(i18n(", ")).join(secondarygroups))
496
self.deletebutton.setDisabled(userobj.getUID()==0)
498
#######################################################################
499
def __updateGroupList(self):
500
self.grouplist.clear()
501
self.groupidsToListItems = {}
502
firstselectedgroupid = None
504
groups = self.admincontext.getGroups()
505
for groupobj in groups:
506
gid = groupobj.getGID()
507
if self.showsystemgroups or not groupobj.isSystemGroup():
508
lvi = QListViewItem(self.grouplist,groupobj.getGroupname(),unicode(gid))
509
self.groupidsToListItems[gid] = lvi
510
if self.selectedgroupid==gid:
511
firstselectedgroupid = gid
512
elif firstselectedgroupid==None:
513
firstselectedgroupid = gid
514
self.selectedgroupid = firstselectedgroupid
515
self.__selectGroup(self.selectedgroupid)
516
self.grouplist.ensureItemVisible(self.grouplist.currentItem())
518
#######################################################################
519
def __selectGroup(self,groupid):
521
self.selectedgroupid = groupid
522
lvi = self.groupidsToListItems[groupid]
523
self.grouplist.setSelected(lvi,True)
524
self.grouplist.setCurrentItem(lvi)
526
groupobj = self.admincontext.lookupGID(groupid)
527
members = groupobj.getUsers()
528
self.groupmemberlist.clear()
529
for userobj in members:
531
lvi = QListViewItem(self.groupmemberlist,userobj.getUsername(),userobj.getRealName(),unicode(userobj.getUID()))
533
self.deletegroupbutton.setDisabled(groupobj.getGID()==0)
535
#######################################################################
536
def __loadOptions(self):
537
self.config.setGroup("General")
538
size = self.config.readSizeEntry("Geometry")
539
if size.isEmpty()==False:
541
self.config.setGroup("Options")
542
self.showsystemaccounts = self.config.readBoolEntry("ShowSystemAccounts")
543
self.showspecialcheckbox.setChecked(self.showsystemaccounts)
544
self.showsystemgroups = self.config.readBoolEntry("ShowSystemGroups")
545
self.showspecialgroupscheckbox.setChecked(self.showsystemgroups)
547
#######################################################################
548
def __saveOptions(self):
552
self.config.setGroup("General")
553
self.config.writeEntry("Geometry", self.size())
554
self.config.setGroup("Options")
555
self.config.writeEntry("ShowSystemAccounts",self.showsystemaccounts)
556
self.config.writeEntry("ShowSystemGroups",self.showsystemgroups)
559
#######################################################################
560
# KControl virtual void methods
567
def sysdefaults(self):
571
# Return the KAboutData object which we created during initialisation.
572
return self.aboutdata
574
# Only supply a Help button. Other choices are Default and Apply.
577
###########################################################################
579
# Rudd-O convenience class to map groups to privilege names
580
class PrivilegeNames(dict):
581
"""Convenience dict-derived class: map known secondary groups to privilege names, provide default mapping for groups that do not have a description. This could be replaced by a simple dict() but I simply preferred the class declaration.
583
FIXME This should ideally be included in a more general module so it can be reused."""
586
dict.__init__(self, {
587
"plugdev":i18n("Access external storage devices automatically"),
588
"adm":i18n("Administer the system"),
589
"ltsp":i18n("Allow use of FUSE filesystems like LTSP thin client block devices"),
590
"dialout":i18n("Connect to the Internet using a modem"),
591
"syslog":i18n("Monitor system logs"),
592
"fax":i18n("Send and receive faxes"),
593
"cdrom":i18n("Use CD-ROM and DVD drives"),
594
"floppy":i18n("Use floppy drives"),
595
"modem":i18n("Use modems"),
596
"scanner":i18n("Use scanners"),
599
def __getitem__(self,name):
600
# This is cruft but I couldn't bring myself to kill it bua!
601
if name in self: return dict.__getitem__(self,name)
602
return i18n("Be a member of the %s group")%name
604
class UserEditDialog(KDialogBase):
605
def __init__(self,parent,admincontext):
606
KDialogBase.__init__(self,KJanusWidget.Tabbed,i18n("User Account"),KDialogBase.Ok|KDialogBase.Cancel,
607
KDialogBase.Cancel,parent)
609
self.admincontext = admincontext
610
self.updatingGUI = True
612
detailsvbox = self.addHBoxPage(i18n("Details"))
613
detailspace = QWidget(detailsvbox)
615
infogrid = QGridLayout(detailspace,9,2)
616
infogrid.setSpacing(self.spacingHint())
617
infogrid.setColStretch(0,0)
618
infogrid.setColStretch(1,1)
620
self.enabledradiogroup = QButtonGroup()
621
self.enabledradiogroup.setRadioButtonExclusive(True)
622
hb = QHBox(detailspace)
623
hb.setSpacing(self.spacingHint())
625
label.setPixmap(UserIcon("hi32-identity"))
626
hb.setStretchFactor(label,0)
627
label = QLabel(i18n("Status:"),hb)
628
hb.setStretchFactor(label,1)
629
infogrid.addMultiCellWidget(hb,0,1,0,0)
631
self.enabledradio = QRadioButton(i18n("Enabled"),detailspace)
632
infogrid.addWidget(self.enabledradio,0,1)
634
hbox = QHBox(detailspace)
635
hbox.setSpacing(self.spacingHint())
636
self.disabledradio = QRadioButton(i18n("Disabled"),hbox)
637
hbox.setStretchFactor(self.disabledradio,0)
639
label.setPixmap(UserIcon("hi16-encrypted"))
640
hbox.setStretchFactor(label,1)
641
infogrid.addWidget(hbox,1,1)
643
self.enabledradiogroup.insert(self.enabledradio,0)
644
self.enabledradiogroup.insert(self.disabledradio,1)
646
label = QLabel(i18n("Login Name:"),detailspace)
647
infogrid.addWidget(label,2,0)
648
self.loginnameedit = KLineEdit("",detailspace)
649
self.loginnameedit.setValidator(LoginNameValidator(self.loginnameedit))
651
infogrid.addWidget(self.loginnameedit,2,1)
652
self.connect(self.loginnameedit, SIGNAL("textChanged(const QString &)"), self.slotLoginChanged)
654
label = QLabel(i18n("Real Name:"),detailspace)
655
infogrid.addWidget(label,3,0)
656
self.realnameedit = KLineEdit("",detailspace)
657
self.realnameedit.setValidator(RealUserNameValidator(self.realnameedit))
659
infogrid.addWidget(self.realnameedit,3,1)
661
label = QLabel(i18n("User ID:"),detailspace)
662
infogrid.addWidget(label,4,0)
663
self.uidedit = KLineEdit("",detailspace)
664
self.uidedit.setValidator(QIntValidator(0,65535,detailspace))
665
infogrid.addWidget(self.uidedit,4,1)
667
label = QLabel(i18n("Primary Group:"),detailspace)
668
infogrid.addWidget(label,5,0)
669
self.primarygroupedit = KComboBox(False,detailspace)
670
infogrid.addWidget(self.primarygroupedit,5,1)
672
label = QLabel(i18n("Home Directory:"),detailspace)
673
infogrid.addWidget(label,7,0)
675
hbox = QHBox(detailspace)
676
hbox.setSpacing(self.spacingHint())
677
self.homediredit = KLineEdit("",hbox)
678
hbox.setStretchFactor(self.homediredit,1)
679
self.connect(self.homediredit, SIGNAL("textChanged(const QString &)"), self.slotHomeDirChanged)
680
self.homedirbutton = KPushButton(i18n("Browse..."),hbox)
681
hbox.setStretchFactor(self.homedirbutton,0)
682
self.connect(self.homedirbutton,SIGNAL("clicked()"),self.slotBrowseHomeDirClicked)
683
infogrid.addWidget(hbox,7,1)
685
label = QLabel(i18n("Shell:"),detailspace)
686
infogrid.addWidget(label,8,0)
688
self.shelledit = KComboBox(True,detailspace)
689
for shell in self.admincontext.getUserShells():
690
self.shelledit.insertItem(shell)
691
infogrid.addWidget(self.shelledit,8,1)
693
# Rudd-O rules. Not so much, but enough to rule.
694
# yeah it's not my finest hour, but it works like a charm over here. Please feel free to clean up dead code that I commented
695
# I extend my deepest thanks to the people that have worked hard to construct this tool in the first place. I have no idea who the authors and contributors are, but it would make sense to have all the contributors listed on top of the file.
696
# Privileges and groups tab
697
groupsvbox = self.addHBoxPage(i18n("Privileges and groups"))
699
# Rudd-O now here we create the widget that will hold the group listing, and fill it with the groups.
700
self.privilegeslistview = QListView(groupsvbox)
701
self.privilegeslistview.addColumn(i18n("Privilege"),-1)
702
self.groupslistview = QListView(groupsvbox)
703
self.groupslistview.addColumn(i18n("Secondary group"),-1)
704
groupsvbox.setStretchFactor(self.privilegeslistview,3)
705
groupsvbox.setStretchFactor(self.groupslistview,2)
707
# Password and Security Tab.
708
passwordvbox = self.addVBoxPage(i18n("Password && Security"))
710
passwordspace = QWidget(passwordvbox)
711
passwordgrid = QGridLayout(passwordspace,8,3)
712
passwordgrid.setSpacing(self.spacingHint())
713
passwordgrid.setColStretch(0,0)
714
passwordgrid.setColStretch(1,0)
715
passwordgrid.setColStretch(2,1)
716
passwordvbox.setStretchFactor(passwordspace,0)
718
hb = QHBox(passwordspace)
719
hb.setSpacing(self.spacingHint())
721
label.setPixmap(UserIcon("hi32-password"))
722
hb.setStretchFactor(label,0)
723
label = QLabel(i18n("Password:"),hb)
724
hb.setStretchFactor(label,1)
725
passwordgrid.addWidget(hb,0,0)
727
self.passwordedit = KPasswordEdit(passwordspace)
728
passwordgrid.addWidget(self.passwordedit,0,1)
731
label = QLabel(i18n("Last changed:"),passwordspace)
732
passwordgrid.addWidget(label,1,0)
733
self.lastchangelabel = KLineEdit("",passwordspace)
734
self.lastchangelabel.setReadOnly(True)
735
passwordgrid.addWidget(self.lastchangelabel,1,1)
737
self.validradiogroup = QButtonGroup()
738
self.validradiogroup.setRadioButtonExclusive(True)
741
label = QLabel(i18n("Valid until:"),passwordspace)
742
passwordgrid.addWidget(label,2,0)
743
self.validalwaysradio = QRadioButton(i18n("Always"),passwordspace)
744
passwordgrid.addWidget(self.validalwaysradio,2,1)
746
hbox = QHBox(passwordspace)
747
hbox.setSpacing(self.spacingHint())
748
self.expireradio = QRadioButton(hbox)
749
hbox.setStretchFactor(self.expireradio,0)
751
self.expiredate = KDateWidget(hbox)
752
hbox.setStretchFactor(self.expiredate,1)
753
passwordgrid.addWidget(hbox,3,1)
755
self.validradiogroup.insert(self.validalwaysradio,0)
756
self.validradiogroup.insert(self.expireradio,1)
757
self.connect(self.validradiogroup,SIGNAL("clicked(int)"),self.slotValidUntilClicked)
759
# Password Aging & Expiration.
760
passwordaginggroup = QVGroupBox(i18n("Password Aging"),passwordvbox)
761
passwordaginggroup.setInsideSpacing(self.spacingHint())
762
passwordvbox.setStretchFactor(passwordaginggroup,0)
764
passwordagingwidget = QWidget(passwordaginggroup)
766
passwordaginggrid = QGridLayout(passwordagingwidget,4,3)
767
passwordaginggrid.setSpacing(self.spacingHint())
769
# [*] Require new password after: [_____5 days]
770
self.forcepasswordchangecheckbox = QCheckBox(passwordagingwidget)
771
self.connect(self.forcepasswordchangecheckbox,SIGNAL("toggled(bool)"),self.slotForcePasswordChangeToggled)
772
passwordaginggrid.addWidget(self.forcepasswordchangecheckbox,0,0)
773
label = QLabel(i18n("Require new password after:"),passwordagingwidget)
774
passwordaginggrid.addWidget(label,0,1)
775
self.maximumpasswordedit = QSpinBox(passwordagingwidget)
776
self.maximumpasswordedit.setSuffix(i18n(" days"))
777
self.maximumpasswordedit.setMinValue(1)
778
self.maximumpasswordedit.setMaxValue(365*5)
779
passwordaginggrid.addWidget(self.maximumpasswordedit,0,2)
781
label = QLabel(i18n("Warn before password expires:"),passwordagingwidget)
782
passwordaginggrid.addWidget(label,1,1)
783
self.warningedit = QSpinBox(passwordagingwidget)
784
self.warningedit.setPrefix(i18n("After "))
785
self.warningedit.setSuffix(i18n(" days"))
786
self.warningedit.setMinValue(0)
787
self.warningedit.setMaxValue(365*5)
788
self.warningedit.setSpecialValueText(i18n("Never"))
789
passwordaginggrid.addWidget(self.warningedit,1,2)
791
label = QLabel(i18n("Disable account after password expires:"),passwordagingwidget)
792
passwordaginggrid.addWidget(label,2,1)
793
self.disableexpireedit = QSpinBox(passwordagingwidget)
794
self.disableexpireedit.setPrefix(i18n("After "))
795
self.disableexpireedit.setSuffix(i18n(" days"))
796
self.disableexpireedit.setMinValue(0)
797
self.disableexpireedit.setMaxValue(365*5)
798
self.disableexpireedit.setSpecialValueText(i18n("Never"))
799
passwordaginggrid.addWidget(self.disableexpireedit,2,2)
801
self.enforcepasswordminagecheckbox = QCheckBox(passwordagingwidget)
802
self.connect(self.enforcepasswordminagecheckbox,SIGNAL("toggled(bool)"),self.slotEnforePasswordAgeToggled)
803
passwordaginggrid.addWidget(self.enforcepasswordminagecheckbox,3,0)
805
label = QLabel(i18n("Enforce minimum password age:"),passwordagingwidget)
806
passwordaginggrid.addWidget(label,3,1)
807
self.minimumpasswordedit = QSpinBox(passwordagingwidget)
808
self.minimumpasswordedit.setSuffix(i18n(" days"))
809
passwordaginggrid.addWidget(self.minimumpasswordedit,3,2)
811
spacer = QWidget(passwordvbox)
812
passwordvbox.setStretchFactor(spacer,1)
814
self.homedirdialog = KDirSelectDialog("/",True,self,"Select Home Directory",True)
815
self.createhomedirectorydialog = OverwriteHomeDirectoryDialog(None)
816
self.updatingGUI = False
818
def _repopulateGroupsPrivileges(self,excludegroups=None):
819
# needs listviews to be constructed. Expects a list of PwdGroups to be excluded
822
self.privilegeslistview.clear()
823
self.groupslistview.clear()
824
self.secondarygroupcheckboxes = {}
825
pn = PrivilegeNames()
827
if excludegroups: excludegroups = [ g.getGroupname() for g in excludegroups ]
828
else: excludegroups = []
829
for group in [g.getGroupname() for g in self.admincontext.getGroups()]:
830
if group in excludegroups: continue
832
name = i18n(unicode(pn[group]).encode(locale.getpreferredencoding()))
833
wid = self.privilegeslistview
835
name = unicode(group).encode(locale.getpreferredencoding())
836
wid = self.groupslistview
837
self.secondarygroupcheckboxes[group] = QCheckListItem(wid,name,QCheckListItem.CheckBox)
839
########################################################################
840
def showEditUser(self,userid):
841
self.updatingGUI = True
842
self.newusermode = False
843
self.userobj = self.admincontext.lookupUID(userid)
845
self.passwordedit.erase()
846
self.selectedgroups = [g.getGroupname() for g in self.userobj.getGroups()
847
if g is not self.userobj.getPrimaryGroup()]
849
# Rudd-O: now here we tick the appropriate group listing checkbox, and hide the currently active primary group of the user. We are repopulating because if the user to edit changes, we need to hide the user's secondary group. FIXME we should repopulate the groups privileges list when the primary group is changed in the other tab -- that is, on the change slot of the primary group drop down.
850
self._repopulateGroupsPrivileges(excludegroups=[self.userobj.getPrimaryGroup()])
851
for group,checkbox in self.secondarygroupcheckboxes.items():
852
if group in self.selectedgroups: checkbox.setState(QCheckListItem.On)
853
else: checkbox.setState(QCheckListItem.Off)
855
self.originalgroups = self.selectedgroups[:]
856
self.selectedgroups.sort()
858
self.uidedit.setReadOnly(True)
859
self.updatingGUI = False
860
self.homedirectoryislinked = False
861
if self.exec_loop()==QDialog.Accepted:
862
self.__updateObjectFromGUI(self.userobj)
864
if self.passwordedit.password()!="":
865
self.userobj.setPassword(self.passwordedit.password())
866
# Update the groups for this user object. Rudd-O here's when you go in, stud.
867
# we collect the selected groups
868
self.selectedgroups = [ group for group,checkbox in self.secondarygroupcheckboxes.items() if checkbox.isOn() ]
870
for g in self.userobj.getGroups(): # this seems wasteful to remove the user from all groups then re-add, why not a cross check?
871
self.userobj.removeFromGroup(g)
872
for gn in self.selectedgroups:
873
self.userobj.addToGroup(self.admincontext.lookupGroupname(gn))
875
primarygroupname = unicode(self.primarygroupedit.currentText())
876
self.userobj.setPrimaryGroup(self.admincontext.lookupGroupname(primarygroupname))
878
# Enable/Disable the account
879
self.userobj.setLocked(self.enabledradiogroup.id(self.enabledradiogroup.selected())!=0)
880
self.admincontext.save()
882
########################################################################
883
def showNewUser(self):
884
self.updatingGUI = True
885
self.newusermode = True
886
self.userobj = self.admincontext.newUser(True)
888
self.newgroup = self.admincontext.newGroup(True)
889
self.newgroup.setGroupname(self.__fudgeNewGroupName(self.userobj.getUsername()))
890
self.userobj.setPrimaryGroup(self.newgroup)
892
self.selectedgroups = [ u'dialout',u'cdrom',u'floppy',u'audio',u'video',
893
u'plugdev',u'lpadmin',u'scanner']
894
homedir = self.__fudgeNewHomeDirectory(self.userobj.getUsername())
896
# Rudd-O FIXME: now here we tick the proper groups that should be allowed. Now it selects what userconfig selected before. FIXME consider adding a drop down that will select the appropriate profile Limited User, Advanced User or Administrator (and see if there is a config file where these profiles can be read). We are repopulating because if the user to edit changes, we need to hide the user's secondary group. FIXME we should repopulate the groups privileges list when the primary group is changed in the other tab -- that is, on the change slot of the primary group drop down.
897
self._repopulateGroupsPrivileges()
898
for group,checkbox in self.secondarygroupcheckboxes.items():
899
if group in self.selectedgroups: checkbox.setState(QCheckListItem.On)
900
else: checkbox.setState(QCheckListItem.Off)
902
self.userobj.setHomeDirectory(homedir)
903
self.homediredit.setText(homedir)
905
shells = self.admincontext.getUserShells()
906
dshell = self.admincontext.dshell
907
if dshell and (dshell in shells):
908
self.userobj.setLoginShell(dshell)
909
elif '/bin/bash' in shells:
910
self.userobj.setLoginShell('/bin/bash')
911
elif '/bin/sh' in shells:
912
self.userobj.setLoginShell('/bin/sh')
914
self.userobj.setLoginShell(shells[0])
918
self.uidedit.setReadOnly(False)
919
self.updatingGUI = False
920
self.homedirectoryislinked = True
921
self.passwordedit.erase()
922
if self.exec_loop()==QDialog.Accepted:
923
self.__updateObjectFromGUI(self.userobj)
926
deleteoldhomedir = False
928
if os.path.exists(self.userobj.getHomeDirectory()):
929
rc = self.createhomedirectorydialog.do(self.userobj)
930
if rc==OverwriteHomeDirectoryDialog.CANCEL:
932
if rc==OverwriteHomeDirectoryDialog.OK_KEEP:
935
deleteoldhomedir = True
937
self.admincontext.addUser(self.userobj)
939
if self.admincontext.lookupGroupname(self.primarygroupname) is None:
941
newgroup = self.admincontext.newGroup(True)
942
newgroup.setGroupname(self.primarygroupname)
943
self.admincontext.addGroup(newgroup)
944
self.userobj.setPrimaryGroup(newgroup)
946
# Update the groups for this user object. Rudd-O here's when you go in, stud.
947
# we collect the selected groups
948
self.selectedgroups = [ group for group,checkbox in self.secondarygroupcheckboxes.items() if checkbox.isOn() ]
949
for gn in self.selectedgroups:
950
self.userobj.addToGroup(self.admincontext.lookupGroupname(gn))
953
if self.passwordedit.password()!="":
954
self.userobj.setPassword(self.passwordedit.password())
956
# Enable/Disable the account
957
self.userobj.setLocked(self.enabledradiogroup.id(self.enabledradiogroup.selected())!=0)
958
self.admincontext.save()
961
if os.path.exists(self.userobj.getHomeDirectory()):
962
shutil.rmtree(self.userobj.getHomeDirectory())
964
self.admincontext.createHomeDirectory(self.userobj)
966
return self.userobj.getUID()
970
########################################################################
973
# Sanity check all values.
975
newusername = unicode(self.realnameedit.text())
976
if self.admincontext.lookupUsername(newusername)!=None:
977
KMessageBox.sorry(self,i18n("Sorry, you must choose a different user name.\n'%1' is already being used.").arg(newusername))
980
newuid = int(unicode(self.uidedit.text()))
981
originaluid = self.userobj.getUID()
982
if self.admincontext.lookupUID(newuid)!=None:
983
rc = KMessageBox.questionYesNo(self,i18n("User ID in use"),
984
i18n("Sorry, the UID %1 is already in use. Should %2 be used instead?").arg(newuid).arg(originaluid))
985
if rc==KMessageBox.Yes:
986
self.uidedit.setValue(unicode(originaluid))
990
self.userobj.setUID(newuid)
992
self.passwordedit.clear()
993
KDialogBase.slotOk(self)
995
########################################################################
996
def slotLoginChanged(self,text):
997
newtext = unicode(text)
998
if not self.updatingGUI:
1000
self.newprimarygroupname = self.__fudgeNewGroupName(newtext)
1001
self.updatingGUI = True
1002
self.primarygroupedit.changeItem(self.newprimarygroupname,0)
1003
if self.homedirectoryislinked:
1004
homedir = self.__fudgeNewHomeDirectory(newtext)
1005
self.homediredit.setText(homedir)
1006
self.updatingGUI = False
1008
########################################################################
1009
def slotHomeDirChanged(self,text):
1010
if self.updatingGUI==False:
1011
self.homedirectoryislinked = False
1013
########################################################################
1014
def __syncGUI(self):
1015
if self.userobj.isLocked():
1016
self.enabledradiogroup.setButton(1)
1018
self.enabledradiogroup.setButton(0)
1020
self.loginnameedit.setText(self.userobj.getUsername())
1021
self.realnameedit.setText(self.userobj.getRealName())
1022
self.uidedit.setText(unicode(self.userobj.getUID()))
1023
self.homediredit.setText(self.userobj.getHomeDirectory())
1024
self.shelledit.setCurrentText(self.userobj.getLoginShell())
1027
self.primarygroupedit.clear()
1028
allgroups = [g.getGroupname() for g in self.admincontext.getGroups()]
1030
self.availablegroups = allgroups[:]
1033
self.availablegroups.remove(self.userobj.getPrimaryGroup().getGroupname())
1037
if self.newusermode:
1039
self.newprimarygroupname = self.__fudgeNewGroupName(unicode(self.userobj.getUsername()))
1040
primarygroupname = self.newprimarygroupname
1041
self.primarygroupedit.insertItem(self.newprimarygroupname)
1043
# Existing user mode
1044
primarygroupname = self.userobj.getPrimaryGroup().getGroupname()
1045
for group in allgroups:
1046
self.primarygroupedit.insertItem(group)
1047
self.primarygroupedit.setCurrentText(primarygroupname)
1049
# If ShadowExpire is turn off then we change the radio box.
1050
if self.userobj.getExpirationDate() is None:
1051
self.validradiogroup.setButton(0)
1052
self.expiredate.setDisabled(True)
1053
self.expiredate.setDate(SptimeToQDate(99999L))
1055
self.validradiogroup.setButton(1)
1056
self.expiredate.setDisabled(False)
1057
self.expiredate.setDate(SptimeToQDate(self.userobj.getExpirationDate()))
1059
if self.userobj.getMaximumPasswordAge() is None:
1060
# Password aging is turn off
1061
self.forcepasswordchangecheckbox.setChecked(False)
1064
# Password aging is turn on
1065
self.forcepasswordchangecheckbox.setChecked(True)
1067
self.warningedit.setDisabled(d)
1068
self.maximumpasswordedit.setDisabled(d)
1069
self.disableexpireedit.setDisabled(d)
1071
if self.userobj.getPasswordExpireWarning() is None:
1072
self.warningedit.setValue(0)
1074
self.warningedit.setValue(self.userobj.getPasswordExpireWarning())
1076
if self.userobj.getMaximumPasswordAge() is None:
1077
self.maximumpasswordedit.setValue(30)
1079
self.maximumpasswordedit.setValue(self.userobj.getMaximumPasswordAge())
1081
if self.userobj.getPasswordDisableAfterExpire() is None:
1082
self.disableexpireedit.setValue(0)
1084
self.disableexpireedit.setValue(self.userobj.getPasswordDisableAfterExpire())
1086
minage = self.userobj.getMinimumPasswordAgeBeforeChange()
1087
self.enforcepasswordminagecheckbox.setChecked(minage>0)
1088
self.minimumpasswordedit.setDisabled(minage<=0)
1091
self.minimumpasswordedit.setValue(minage)
1093
if self.userobj.getLastPasswordChange() in (None,0):
1094
self.lastchangelabel.setText('-');
1096
self.lastchangelabel.setText(KGlobal.locale().formatDate(SptimeToQDate(int(self.userobj.getLastPasswordChange()))))
1098
########################################################################
1099
def __updateObjectFromGUI(self,userobj):
1100
username = unicode(self.loginnameedit.text())
1101
userobj.setUsername(username)
1102
userobj.setRealName(unicode(self.realnameedit.text()))
1104
userobj.setHomeDirectory(unicode(self.homediredit.text()))
1105
userobj.setLoginShell(unicode(self.shelledit.currentText()))
1106
self.primarygroupname = unicode(self.primarygroupedit.currentText())
1107
groupobj = self.admincontext.lookupGroupname(self.primarygroupname)
1108
if groupobj is not None:
1109
userobj.setPrimaryGroup(groupobj)
1111
# Password expiration.
1112
if self.validradiogroup.id(self.validradiogroup.selected())==0:
1113
# Password is always valid.
1114
userobj.setExpirationDate(None)
1116
# Password will expire at...
1117
userobj.setExpirationDate(QDateToSptime(self.expiredate.date()))
1119
if self.forcepasswordchangecheckbox.isChecked():
1120
userobj.setMaximumPasswordAge(self.maximumpasswordedit.value())
1122
userobj.setMaximumPasswordAge(None)
1124
if self.disableexpireedit.value()==0:
1125
userobj.setPasswordDisableAfterExpire(None)
1127
userobj.setPasswordDisableAfterExpire(self.disableexpireedit.value())
1129
if self.enforcepasswordminagecheckbox.isChecked():
1130
userobj.setMinimumPasswordAgeBeforeChange(self.minimumpasswordedit.value())
1132
userobj.setMinimumPasswordAgeBeforeChange(0)
1134
userobj.setPasswordExpireWarning(self.warningedit.value())
1136
########################################################################
1137
def slotBrowseHomeDirClicked(self):
1139
fileurl.setPath(self.homediredit.text())
1140
self.homedirdialog.setCurrentURL(fileurl)
1141
if self.homedirdialog.exec_loop()==QDialog.Accepted:
1142
self.homediredit.setText(self.homedirdialog.url().path())
1143
self.homedirectoryislinked = False
1145
########################################################################
1146
def slotValidUntilClicked(self,id):
1148
self.expiredate.setDisabled(True)
1150
self.expiredate.setDisabled(False)
1152
########################################################################
1153
def slotForcePasswordChangeToggled(self,on):
1155
self.warningedit.setDisabled(on)
1156
self.maximumpasswordedit.setDisabled(on)
1157
self.disableexpireedit.setDisabled(on)
1159
########################################################################
1160
def slotEnforePasswordAgeToggled(self,on):
1161
self.minimumpasswordedit.setDisabled(not on)
1163
#######################################################################
1164
def __fudgeNewGroupName(self,basename):
1165
if self.admincontext.lookupGroupname(basename) is None:
1168
while self.admincontext.lookupGroupname(basename + u'_' + unicode(x)) is not None:
1170
return basename + u'_' + unicode(x)
1172
#######################################################################
1173
def __fudgeNewHomeDirectory(self,origbasename):
1174
basename = origbasename.replace("/","")
1178
dhome = self.admincontext.dhome
1179
if not os.path.isdir(dhome):
1180
raise OSError, dhome+" does not exist, is it correctly set in "+ \
1181
self.admincontext.adduserconf+" ?"
1183
# Make sure there's a trailing /
1184
if dhome[-1] is not '/':
1187
if os.path.exists(dhome+basename)==False:
1188
return dhome+basename
1191
while os.path.exists(dhome+basename + u'_' + unicode(x)):
1193
return dhome+basename
1196
###########################################################################
1197
class LoginNameValidator(QValidator):
1198
def __init__(self,parent):
1199
QValidator.__init__(self,parent)
1201
#######################################################################
1202
def validate(self,inputstr,pos):
1203
instr = unicode(inputstr)
1205
return (QValidator.Intermediate,pos)
1207
if ord(c)<0x20 or ord(c)>0x7f or c.isspace() or c==":" or c=="," or c==".":
1208
return (QValidator.Invalid,pos)
1210
# Try to encode this string in the system encoding.
1212
instr.encode(locale.getpreferredencoding())
1213
except UnicodeEncodeError:
1214
# won't encode -> reject it.
1215
return (QValidator.Invalid,pos)
1217
return (QValidator.Acceptable,pos)
1219
#######################################################################
1220
def fixup(self,inputstr):
1221
instr = unicode(inputstr)
1224
if (ord(c)<0x20 or ord(c)>0x7f or c.isspace() or c==":" or c=="," or c==".")==False:
1232
###########################################################################
1233
class RealUserNameValidator(QValidator):
1234
def __init__(self,parent):
1235
QValidator.__init__(self,parent)
1237
#######################################################################
1238
def validate(self,inputstr,pos):
1239
instr = unicode(inputstr)
1242
return (QValidator.Invalid,pos)
1244
# Try to encode this string in the system encoding.
1246
instr.encode(locale.getpreferredencoding())
1247
except UnicodeEncodeError:
1248
# won't encode -> reject it.
1249
return (QValidator.Invalid,pos)
1251
return (QValidator.Acceptable,pos)
1253
#######################################################################
1254
def fixup(self,inputstr):
1255
return unicode(inputstr).replace(":","")
1257
###########################################################################
1258
class ListPickerDialog(KDialogBase):
1259
def __init__(self,parent,caption,leftlabel,rightlabel):
1260
KDialogBase.__init__(self,parent,None,True,caption,KDialogBase.Ok|KDialogBase.Cancel, KDialogBase.Cancel)
1262
self.tophbox = QHBox(self)
1263
self.setMainWidget(self.tophbox)
1264
self.tophbox.setSpacing(self.spacingHint())
1266
vbox = QVBox(self.tophbox)
1267
self.tophbox.setStretchFactor(vbox,1)
1268
label = QLabel(leftlabel,vbox)
1269
vbox.setStretchFactor(label,0)
1270
self.availablelist = KListBox(vbox)
1271
vbox.setStretchFactor(self.availablelist,1)
1274
vbox = QVBox(self.tophbox)
1275
self.tophbox.setStretchFactor(vbox,0)
1276
spacer = QWidget(vbox);
1277
vbox.setStretchFactor(spacer,1)
1278
self.addbutton = KPushButton(i18n("Add ->"),vbox)
1279
self.connect(self.addbutton,SIGNAL("clicked()"),self.slotAddClicked)
1280
vbox.setStretchFactor(self.addbutton,0)
1281
self.removebutton = KPushButton(i18n("<- Remove"),vbox)
1282
self.connect(self.removebutton,SIGNAL("clicked()"),self.slotRemoveClicked)
1283
vbox.setStretchFactor(self.removebutton,0)
1284
spacer = QWidget(vbox);
1285
vbox.setStretchFactor(spacer,1)
1288
vbox = QVBox(self.tophbox)
1289
self.tophbox.setStretchFactor(vbox,1)
1290
label = QLabel(rightlabel,vbox)
1291
vbox.setStretchFactor(label,0)
1292
self.selectedlist = KListBox(vbox)
1293
vbox.setStretchFactor(self.selectedlist,1)
1295
#######################################################################
1296
def do(self,grouplist,selectedlist):
1297
self.selectedlist.clear()
1298
for item in selectedlist:
1299
self.selectedlist.insertItem(item)
1300
self.selectedlist.sort()
1302
self.availablelist.clear()
1303
for item in grouplist:
1304
if item not in selectedlist:
1305
self.availablelist.insertItem(item)
1306
self.availablelist.sort()
1308
self._selectFirstAvailable()
1309
self.addbutton.setDisabled(self.availablelist.selectedItem()==None)
1311
self._selectFirstSelected()
1312
self.removebutton.setDisabled(self.selectedlist.selectedItem()==None)
1314
if self.exec_loop()==QDialog.Accepted:
1316
for i in range(self.selectedlist.count()):
1317
newlist.append(unicode(self.selectedlist.item(i).text()))
1322
#######################################################################
1323
def slotAddClicked(self):
1324
item = self.availablelist.selectedItem()
1326
self.selectedlist.insertItem(item.text())
1327
self.availablelist.removeItem(self.availablelist.index(item))
1328
self._selectFirstAvailable()
1329
self._selectFirstSelected()
1330
self.addbutton.setDisabled(self.availablelist.selectedItem()==None)
1331
self.removebutton.setDisabled(self.selectedlist.selectedItem()==None)
1333
#######################################################################
1334
def slotRemoveClicked(self):
1335
item = self.selectedlist.selectedItem()
1337
self.availablelist.insertItem(item.text())
1338
self.selectedlist.removeItem(self.selectedlist.index(item))
1339
self._selectFirstAvailable()
1340
self._selectFirstSelected()
1341
self.addbutton.setDisabled(self.availablelist.selectedItem()==None)
1342
self.removebutton.setDisabled(self.selectedlist.selectedItem()==None)
1344
#######################################################################
1345
def _selectFirstAvailable(self):
1346
if self.availablelist.count()!=0:
1347
if self.availablelist.selectedItem()==None:
1348
self.availablelist.setSelected(0,True)
1350
#######################################################################
1351
def _selectFirstSelected(self):
1352
if self.selectedlist.count()!=0:
1353
if self.selectedlist.selectedItem()==None:
1354
self.selectedlist.setSelected(0,True)
1356
###########################################################################
1357
class UserDeleteDialog(KDialog):
1358
def __init__(self,parent,admincontext):
1359
KDialog.__init__(self,parent,"Delete user dialog",True,Qt.WType_Dialog)
1360
self.setCaption(i18n("Delete User Account"))
1361
self.admincontext = admincontext
1362
self.updatingGUI = True
1364
toplayout = QVBoxLayout(self)
1365
toplayout.setSpacing(self.spacingHint())
1366
toplayout.setMargin(self.marginHint())
1368
contentbox = QHBox(self)
1369
contentbox.setSpacing(self.spacingHint())
1370
toplayout.addWidget(contentbox)
1371
toplayout.setStretchFactor(contentbox,1)
1373
label = QLabel(contentbox)
1374
label.setPixmap(KGlobal.iconLoader().loadIcon("messagebox_warning", KIcon.NoGroup, KIcon.SizeMedium,
1375
KIcon.DefaultState, None, True))
1376
contentbox.setStretchFactor(label,0)
1378
textbox = QVBox(contentbox)
1380
textbox.setSpacing(self.spacingHint())
1381
textbox.setMargin(self.marginHint())
1383
self.usernamelabel = QLabel("",textbox)
1384
textbox.setStretchFactor(self.usernamelabel,0)
1386
# Remove directory checkbox.
1387
self.deletedirectorycheckbox = QCheckBox(i18n("Delete home directory ()"),textbox)
1388
textbox.setStretchFactor(self.deletedirectorycheckbox,0)
1390
# Delete the User's private group.
1391
self.deletegroupcheckbox = QCheckBox(i18n("Delete group ()"),textbox)
1392
textbox.setStretchFactor(self.deletegroupcheckbox ,0)
1395
buttonbox = QHBox(self)
1396
toplayout.addWidget(buttonbox)
1398
buttonbox.setSpacing(self.spacingHint())
1399
toplayout.setStretchFactor(buttonbox,0)
1401
spacer = QWidget(buttonbox)
1402
buttonbox.setStretchFactor(spacer,1)
1404
okbutton = QPushButton(i18n("OK"),buttonbox)
1405
buttonbox.setStretchFactor(okbutton,0)
1406
self.connect(okbutton,SIGNAL("clicked()"),self.slotOkClicked)
1408
cancelbutton = QPushButton(i18n("Cancel"),buttonbox)
1409
cancelbutton.setDefault(True)
1410
buttonbox.setStretchFactor(cancelbutton,0)
1411
self.connect(cancelbutton,SIGNAL("clicked()"),self.slotCancelClicked)
1413
def deleteUser(self,userid):
1415
userobj = self.admincontext.lookupUID(userid)
1416
self.usernamelabel.setText(i18n("Are you sure want to delete user account '%1' (%2)?").arg(userobj.getUsername()).arg(userobj.getUID()) )
1417
self.deletedirectorycheckbox.setChecked(False)
1418
self.deletedirectorycheckbox.setText(i18n("Delete home directory (%1)").arg(userobj.getHomeDirectory()))
1419
primarygroupobj = userobj.getPrimaryGroup()
1420
primarygroupname = primarygroupobj.getGroupname()
1421
self.deletegroupcheckbox.setText(i18n("Delete group '%1' (%2)").arg(primarygroupname).arg(primarygroupobj.getGID()))
1422
self.deletegroupcheckbox.setChecked(len(primarygroupobj.getUsers())==1)
1423
if self.exec_loop()==QDialog.Accepted:
1424
self.admincontext.removeUser(userobj)
1425
if self.deletedirectorycheckbox.isChecked():
1426
self.admincontext.removeHomeDirectory(userobj)
1428
#self.admincontext.removeMail(userobj)
1429
if self.deletegroupcheckbox.isChecked():
1430
self.admincontext.removeGroup(primarygroupobj)
1431
self.admincontext.save()
1436
def slotOkClicked(self):
1439
def slotCancelClicked(self):
1442
###########################################################################
1443
class OverwriteHomeDirectoryDialog(KDialog):
1448
def __init__(self,parent):
1449
KDialog.__init__(self,parent,"Create home directory",True,Qt.WType_Dialog)
1450
self.setCaption(i18n("Create home directory"))
1451
self.updatingGUI = True
1453
toplayout = QVBoxLayout(self)
1454
toplayout.setSpacing(self.spacingHint())
1455
toplayout.setMargin(self.marginHint())
1457
contentbox = QHBox(self)
1458
contentbox.setSpacing(self.spacingHint())
1459
toplayout.addWidget(contentbox)
1460
toplayout.setStretchFactor(contentbox,1)
1462
label = QLabel(contentbox)
1463
label.setPixmap(KGlobal.iconLoader().loadIcon("messagebox_warning", KIcon.NoGroup, KIcon.SizeMedium,
1464
KIcon.DefaultState, None, True))
1465
contentbox.setStretchFactor(label,0)
1467
textbox = QVBox(contentbox)
1469
textbox.setSpacing(self.spacingHint())
1470
textbox.setMargin(self.marginHint())
1472
# "%dir was selected as the home directory for %user. This directory already exists. Shall I:."
1473
self.toplabel = QLabel("",textbox)
1474
textbox.setStretchFactor(self.toplabel,0)
1476
self.radiogroup = QButtonGroup()
1477
self.radiogroup.setRadioButtonExclusive(True)
1479
# Use Existing home directory radio button.
1480
self.usehomedirectoryradio = QRadioButton(i18n("Use the existing directory without changing it."),textbox)
1481
textbox.setStretchFactor(self.usehomedirectoryradio,0)
1483
# Replace home directory radio button
1484
self.replacehomedirectoryradio = QRadioButton(i18n("Delete the directory and replace it with a new home directory."),textbox)
1485
textbox.setStretchFactor(self.replacehomedirectoryradio ,0)
1487
self.radiogroup.insert(self.usehomedirectoryradio,0)
1488
self.radiogroup.insert(self.replacehomedirectoryradio,1)
1491
buttonbox = QHBox(self)
1492
toplayout.addWidget(buttonbox)
1494
buttonbox.setSpacing(self.spacingHint())
1495
toplayout.setStretchFactor(buttonbox,0)
1497
spacer = QWidget(buttonbox)
1498
buttonbox.setStretchFactor(spacer,1)
1500
okbutton = QPushButton(i18n("OK"),buttonbox)
1501
buttonbox.setStretchFactor(okbutton,0)
1502
self.connect(okbutton,SIGNAL("clicked()"),self.slotOkClicked)
1504
cancelbutton = QPushButton(i18n("Cancel"),buttonbox)
1505
cancelbutton.setDefault(True)
1506
buttonbox.setStretchFactor(cancelbutton,0)
1507
self.connect(cancelbutton,SIGNAL("clicked()"),self.slotCancelClicked)
1509
def do(self,userobj):
1511
self.toplabel.setText(i18n("The directory '%1' was entered as the home directory for new user '%2'.\n This directory already exists.") \
1512
.arg(userobj.getHomeDirectory()).arg(userobj.getUsername()) )
1513
self.radiogroup.setButton(0)
1515
if self.exec_loop()==QDialog.Accepted:
1516
if self.radiogroup.selectedId()==0:
1517
return OverwriteHomeDirectoryDialog.OK_KEEP
1519
return OverwriteHomeDirectoryDialog.OK_REPLACE
1521
return OverwriteHomeDirectoryDialog.CANCEL
1523
def slotOkClicked(self):
1526
def slotCancelClicked(self):
1529
###########################################################################
1530
class GroupEditDialog(KDialogBase):
1531
def __init__(self,parent,admincontext):
1532
KDialogBase.__init__(self,parent,None,True,i18n("Edit Group"),KDialogBase.Ok|KDialogBase.Cancel,
1535
self.admincontext = admincontext
1537
topvbox = QVBox(self)
1538
topvbox.setSpacing(self.spacingHint())
1539
self.setMainWidget(topvbox)
1541
detailspace = QWidget(topvbox)
1543
# Info about the group.
1544
editgrid = QGridLayout(detailspace,2,2)
1545
editgrid.setSpacing(self.spacingHint())
1547
label = QLabel(i18n("Group Name:"),detailspace)
1548
editgrid.addWidget(label,0,0)
1549
self.groupnamelabel = KLineEdit("",detailspace)
1550
self.groupnamelabel.setReadOnly(True)
1551
editgrid.addWidget(self.groupnamelabel,0,1)
1553
label = QLabel(i18n("Group ID:"),detailspace)
1554
editgrid.addWidget(label,1,0)
1555
self.groupidlabel = KLineEdit("",detailspace)
1556
self.groupidlabel.setReadOnly(True)
1557
editgrid.addWidget(self.groupidlabel,1,1)
1560
tophbox = QHBox(topvbox)
1561
tophbox.setSpacing(self.spacingHint())
1566
vbox.setSpacing(self.spacingHint())
1567
hbox.setStretchFactor(vbox,1)
1568
label = QLabel(i18n("Available Accounts"),vbox)
1569
vbox.setStretchFactor(label,0)
1570
self.availablelist = KListBox(vbox)
1571
vbox.setStretchFactor(self.availablelist,1)
1575
vbox.setSpacing(self.spacingHint())
1576
hbox.setStretchFactor(vbox,0)
1577
spacer = QWidget(vbox);
1578
vbox.setStretchFactor(spacer,1)
1579
self.addbutton = KPushButton(i18n("Add ->"),vbox)
1580
self.connect(self.addbutton,SIGNAL("clicked()"),self.slotAddClicked)
1581
vbox.setStretchFactor(self.addbutton,0)
1582
self.removebutton = KPushButton(i18n("<- Remove"),vbox)
1583
self.connect(self.removebutton,SIGNAL("clicked()"),self.slotRemoveClicked)
1584
vbox.setStretchFactor(self.removebutton,0)
1585
spacer = QWidget(vbox);
1586
vbox.setStretchFactor(spacer,1)
1590
vbox.setSpacing(self.spacingHint())
1591
hbox.setStretchFactor(vbox,1)
1592
label = QLabel(i18n("Selected Accounts"),vbox)
1593
vbox.setStretchFactor(label,0)
1594
self.selectedlist = KListBox(vbox)
1595
vbox.setStretchFactor(self.selectedlist,1)
1597
#######################################################################
1598
def showEditGroup(self,groupid):
1599
self.groupid = groupid
1600
self.newgroupmode = False
1602
groupobj = self.admincontext.lookupGID(groupid)
1604
self.groupnamelabel.setText(groupobj.getGroupname())
1605
self.groupidlabel.setText(unicode(groupid))
1607
availablemembers = [u.getUsername() for u in self.admincontext.getUsers()]
1608
originalmembers = [u.getUsername() for u in groupobj.getUsers()]
1610
self.__updateLists(availablemembers,originalmembers)
1612
if self.exec_loop()==QDialog.Accepted:
1614
for i in range(self.selectedlist.count()):
1615
newmembers.append(unicode(self.selectedlist.item(i).text()))
1617
# Remove from the group object any unselected users.
1618
for member in originalmembers:
1619
if u not in newmembers:
1620
self.admincontext.lookupUsername(member).removeFromGroup(groupobj)
1621
# Put the additional members in the group
1622
for member in newmembers:
1623
self.admincontext.lookupUsername(member).addToGroup(groupobj)
1624
self.admincontext.save()
1629
#######################################################################
1630
def showNewGroup(self):
1631
self.updatingGUI = True
1632
self.newgroupmode = True
1634
groupname = self.__fudgeNewGroupName(i18n("<Base string for creating new group names>","new_group"))
1636
self.groupobj = self.admincontext.newGroup(True)
1637
self.groupobj.setGroupname(groupname)
1639
groupname = self.groupobj.getGroupname()
1640
self.groupnamelabel.setText(groupname)
1641
self.groupnamelabel.setReadOnly(False)
1642
self.groupidlabel.setText(unicode(self.groupobj.getGID()))
1643
self.groupidlabel.setReadOnly(False)
1645
availablemembers = [u.getUsername() for u in self.admincontext.getUsers()]
1647
self.__updateLists(availablemembers,[])
1649
if self.exec_loop()==QDialog.Accepted:
1650
self.groupobj.setGroupname(unicode(self.groupnamelabel.text()))
1651
newgroupid = int(unicode(self.groupidlabel.text()))
1652
self.groupobj.setGID(newgroupid)
1655
for i in range(self.selectedlist.count()):
1656
newmembers.append(unicode(self.selectedlist.item(i).text()))
1658
self.admincontext.addGroup(self.groupobj)
1660
# Put the additional members in the group
1661
for member in newmembers:
1662
self.admincontext.lookupUsername(member).addToGroup(self.groupobj)
1663
self.admincontext.save()
1669
#######################################################################
1670
def slotAddClicked(self):
1671
item = self.availablelist.selectedItem()
1673
self.selectedlist.insertItem(item.text())
1674
self.availablelist.removeItem(self.availablelist.index(item))
1675
self._selectFirstAvailable()
1676
self._selectFirstSelected()
1677
self.addbutton.setDisabled(self.availablelist.selectedItem()==None)
1678
self.removebutton.setDisabled(self.selectedlist.selectedItem()==None)
1680
#######################################################################
1681
def slotRemoveClicked(self):
1682
item = self.selectedlist.selectedItem()
1684
self.availablelist.insertItem(item.text())
1685
self.selectedlist.removeItem(self.selectedlist.index(item))
1686
self._selectFirstAvailable()
1687
self._selectFirstSelected()
1688
self.addbutton.setDisabled(self.availablelist.selectedItem()==None)
1689
self.removebutton.setDisabled(self.selectedlist.selectedItem()==None)
1691
#######################################################################
1692
def __updateLists(self,grouplist,selectedlist):
1693
self.selectedlist.clear()
1694
for item in selectedlist:
1695
self.selectedlist.insertItem(item)
1696
self.selectedlist.sort()
1698
self.availablelist.clear()
1699
for item in grouplist:
1700
if item not in selectedlist:
1701
self.availablelist.insertItem(item)
1702
self.availablelist.sort()
1704
self._selectFirstAvailable()
1705
self.addbutton.setDisabled(self.availablelist.selectedItem()==None)
1707
self._selectFirstSelected()
1708
self.removebutton.setDisabled(self.selectedlist.selectedItem()==None)
1710
#######################################################################
1711
def _selectFirstAvailable(self):
1712
if self.availablelist.count()!=0:
1713
if self.availablelist.selectedItem()==None:
1714
self.availablelist.setSelected(0,True)
1716
#######################################################################
1717
def _selectFirstSelected(self):
1718
if self.selectedlist.count()!=0:
1719
if self.selectedlist.selectedItem()==None:
1720
self.selectedlist.setSelected(0,True)
1722
#######################################################################
1723
def __fudgeNewGroupName(self,basename):
1724
availablegroups = [g.getGroupname() for g in self.admincontext.getGroups()]
1725
if basename not in availablegroups:
1728
while basename + u'_' + unicode(x) in availablegroups:
1730
return basename + u'_' + unicode(x)
1732
############################################################################
1733
# Factory function for KControl
1734
def create_userconfig(parent,name):
1735
return UserConfigApp(parent, name)
1737
##########################################################################
1738
def MakeAboutData():
1739
aboutdata = KAboutData("guidance", programname, version,
1740
unicode(i18n("User and Group Configuration Tool")).encode(locale.getpreferredencoding()),
1741
KAboutData.License_GPL, "Copyright (C) 2003-2007 Simon Edwards")
1742
aboutdata.addAuthor("Simon Edwards", "Developer", "simon@simonzone.com", "http://www.simonzone.com/software/")
1743
aboutdata.addAuthor("Sebastian Kügler", "Developer", "sebas@kde.org", "http://vizZzion.org")
1747
aboutdata = MakeAboutData()
1749
KCmdLineArgs.init(sys.argv,aboutdata)
1751
kapp = KApplication()
1752
userconfigapp = UserConfigApp()
1753
userconfigapp.exec_loop()