forked from public/pyceo
Add experimental urwid-based GUI
This commit is contained in:
parent
299c25d610
commit
588f90b082
6
bin/ceo
6
bin/ceo
|
@ -15,7 +15,7 @@ os.environ['LESSSECURE'] = '1'
|
||||||
os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin'
|
os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin'
|
||||||
|
|
||||||
for pathent in sys.path[:]:
|
for pathent in sys.path[:]:
|
||||||
if not pathent.find('/usr') == 0:
|
if not pathent.find('/usr') == 0 and not pathent.find('/var') == 0:
|
||||||
sys.path.remove(pathent)
|
sys.path.remove(pathent)
|
||||||
|
|
||||||
euid = os.geteuid()
|
euid = os.geteuid()
|
||||||
|
@ -27,5 +27,5 @@ except OSError, e:
|
||||||
print str(e)
|
print str(e)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
import csc.apps.legacy.main
|
import csc.apps.urwid.main
|
||||||
csc.apps.legacy.main.run()
|
csc.apps.urwid.main.start()
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/python2.4 --
|
||||||
|
"""CEO SUID Python Wrapper Script"""
|
||||||
|
import os, sys
|
||||||
|
|
||||||
|
safe_environment = ['LOGNAME', 'USERNAME', 'USER', 'HOME', 'TERM', 'LANG'
|
||||||
|
'LC_ALL', 'LC_COLLATE', 'LC_CTYPE', 'LC_MESSAGES', 'LC_MONETARY',
|
||||||
|
'LC_NUMERIC', 'LC_TIME', 'UID', 'GID', 'SSH_CONNECTION', 'SSH_AUTH_SOCK',
|
||||||
|
'SSH_CLIENT']
|
||||||
|
|
||||||
|
for key in os.environ.keys():
|
||||||
|
if key not in safe_environment:
|
||||||
|
del os.environ[key]
|
||||||
|
|
||||||
|
os.environ['LESSSECURE'] = '1'
|
||||||
|
os.environ['PATH'] = '/usr/sbin:/usr/bin:/sbin:/bin'
|
||||||
|
|
||||||
|
for pathent in sys.path[:]:
|
||||||
|
if not pathent.find('/usr') == 0:
|
||||||
|
sys.path.remove(pathent)
|
||||||
|
|
||||||
|
euid = os.geteuid()
|
||||||
|
egid = os.getegid()
|
||||||
|
try:
|
||||||
|
os.setreuid(euid, euid)
|
||||||
|
os.setregid(egid, egid)
|
||||||
|
except OSError, e:
|
||||||
|
print str(e)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
import csc.apps.legacy.main
|
||||||
|
csc.apps.legacy.main.run()
|
|
@ -7,6 +7,7 @@ build: build-stamp
|
||||||
build-stamp:
|
build-stamp:
|
||||||
mkdir build
|
mkdir build
|
||||||
$(CC) -DFULL_PATH='"/usr/lib/csc/ceo"' -o build/ceo misc/setuid-prog.c
|
$(CC) -DFULL_PATH='"/usr/lib/csc/ceo"' -o build/ceo misc/setuid-prog.c
|
||||||
|
$(CC) -DFULL_PATH='"/usr/lib/csc/ceo-old"' -o build/ceo-old misc/setuid-prog.c
|
||||||
$(CC) -DFULL_PATH='"/usr/lib/csc/addhomedir"' -o build/addhomedir misc/setuid-prog.c
|
$(CC) -DFULL_PATH='"/usr/lib/csc/addhomedir"' -o build/addhomedir misc/setuid-prog.c
|
||||||
$(CC) -DFULL_PATH='"/usr/lib/csc/ceoquery"' -o build/ceoquery misc/setuid-prog.c
|
$(CC) -DFULL_PATH='"/usr/lib/csc/ceoquery"' -o build/ceoquery misc/setuid-prog.c
|
||||||
$(CC) -DFULL_PATH='"/usr/lib/csc/csc-chfn"' -o build/csc-chfn misc/setuid-prog.c
|
$(CC) -DFULL_PATH='"/usr/lib/csc/csc-chfn"' -o build/csc-chfn misc/setuid-prog.c
|
||||||
|
@ -31,8 +32,8 @@ install: build
|
||||||
dh_install etc/* etc/csc/
|
dh_install etc/* etc/csc/
|
||||||
dh_install sql/* usr/share/csc/
|
dh_install sql/* usr/share/csc/
|
||||||
|
|
||||||
dh_install bin/ceo bin/addhomedir bin/ceoquery bin/csc-chsh bin/csc-chfn usr/lib/csc/
|
dh_install bin/ceo bin/ceo-old bin/addhomedir bin/ceoquery bin/csc-chsh bin/csc-chfn usr/lib/csc/
|
||||||
dh_install build/ceo build/addhomedir build/ceoquery build/csc-chsh build/csc-chfn usr/bin/
|
dh_install build/ceo build/ceo-old build/addhomedir build/ceoquery build/csc-chsh build/csc-chfn usr/bin/
|
||||||
dh_install misc/csc.schema etc/ldap/schema/
|
dh_install misc/csc.schema etc/ldap/schema/
|
||||||
|
|
||||||
binary-arch: build install
|
binary-arch: build install
|
||||||
|
|
|
@ -2,7 +2,6 @@ TODO:
|
||||||
|
|
||||||
* Python bindings for libkadm5
|
* Python bindings for libkadm5
|
||||||
* Python bindings for quota?
|
* Python bindings for quota?
|
||||||
* New UI: urwid-based?
|
|
||||||
* Logging via syslog
|
* Logging via syslog
|
||||||
* Try to recover and roll-back on error during account creation
|
* Try to recover and roll-back on error during account creation
|
||||||
* Write manpages
|
* Write manpages
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
"""
|
||||||
|
Urwid User Interface
|
||||||
|
"""
|
|
@ -0,0 +1,39 @@
|
||||||
|
import urwid
|
||||||
|
|
||||||
|
from csc.apps.urwid.widgets import *
|
||||||
|
from csc.apps.urwid.window import *
|
||||||
|
|
||||||
|
from csc.adm import accounts, members
|
||||||
|
from csc.common.excep import InvalidArgument
|
||||||
|
|
||||||
|
class InfoPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.userid = urwid.Text("")
|
||||||
|
self.name = urwid.Text("")
|
||||||
|
self.terms = urwid.Text("")
|
||||||
|
self.program = urwid.Text("")
|
||||||
|
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text( "Member Details" ),
|
||||||
|
urwid.Divider(),
|
||||||
|
self.name,
|
||||||
|
self.userid,
|
||||||
|
self.program,
|
||||||
|
urwid.Divider(),
|
||||||
|
self.terms,
|
||||||
|
]
|
||||||
|
def focusable(self):
|
||||||
|
return False
|
||||||
|
def activate(self):
|
||||||
|
member = self.state.get('member', {})
|
||||||
|
name = member.get('cn', [''])[0]
|
||||||
|
userid = self.state['userid']
|
||||||
|
program = member.get('program', [''])[0]
|
||||||
|
terms = member.get('term', [])
|
||||||
|
|
||||||
|
self.name.set_text("Name: %s" % name)
|
||||||
|
self.userid.set_text("User: %s" % userid)
|
||||||
|
self.program.set_text("Program: %s" % program)
|
||||||
|
self.terms.set_text("Terms: %s" % ", ".join(terms))
|
||||||
|
def check(self):
|
||||||
|
pop_window()
|
|
@ -0,0 +1,122 @@
|
||||||
|
import random, time
|
||||||
|
import urwid, urwid.curses_display
|
||||||
|
|
||||||
|
from csc.apps.urwid.widgets import *
|
||||||
|
from csc.apps.urwid.window import *
|
||||||
|
import csc.apps.urwid.newmember as newmember
|
||||||
|
import csc.apps.urwid.renew as renew
|
||||||
|
import csc.apps.urwid.info as info
|
||||||
|
import csc.apps.urwid.search as search
|
||||||
|
|
||||||
|
from csc.adm import accounts, members, terms
|
||||||
|
from csc.common.excep import InvalidArgument
|
||||||
|
|
||||||
|
ui = urwid.curses_display.Screen()
|
||||||
|
|
||||||
|
ui.register_palette([
|
||||||
|
# name, foreground, background, mono
|
||||||
|
('banner', 'light gray', 'default', None),
|
||||||
|
('menu', 'light gray', 'default', 'bold'),
|
||||||
|
('selected', 'black', 'light gray', 'bold'),
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
def program_name():
|
||||||
|
cwords = [ "CSC" ] * 20 + [ "Club" ] * 10 + [ "Campus" ] * 5 + \
|
||||||
|
[ "Communist", "Canadian", "Celestial", "Cryptographic", "Calum's",
|
||||||
|
"Canonical", "Capitalist", "Catastrophic", "Ceremonial", "Chaotic", "Civic",
|
||||||
|
"City", "County", "Caffeinated" ]
|
||||||
|
ewords = [ "Embellished", "Ergonomic", "Electric", "Eccentric", "European", "Economic",
|
||||||
|
"Evil", "Egotistical", "Elliptic", "Emasculating", "Embalming",
|
||||||
|
"Embryonic", "Emigrant", "Emissary's", "Emoting", "Employment", "Emulated",
|
||||||
|
"Enabling", "Enamoring", "Encapsulated", "Enchanted", "Encoded", "Encrypted",
|
||||||
|
"Encumbered", "Endemic", "Enhanced", "Enigmatic", "Enlightened", "Enormous",
|
||||||
|
"Enrollment", "Enshrouded", "Ephermal", "Epidemic", "Episodic", "Epsilon",
|
||||||
|
"Equitable", "Equestrian", "Equilateral", "Erroneous", "Erratic",
|
||||||
|
"Espresso", "Essential", "Estate", "Esteemed", "Eternal", "Ethical", "Eucalyptus",
|
||||||
|
"Euphemistic", "Envangelist", "Evasive", "Everyday", "Evidence", "Eviction", "Evildoer's",
|
||||||
|
"Evolution", "Exacerbation", "Exalted", "Examiner's", "Excise", "Exciting", "Exclusion",
|
||||||
|
"Exec", "Executioner's", "Exile", "Existential", "Expedient", "Expert", "Expletive",
|
||||||
|
"Exploiter's", "Explosive", "Exponential", "Exposing", "Extortion", "Extraction",
|
||||||
|
"Extraneous", "Extravaganza", "Extreme", "Extraterrestrial", "Extremist", "Eerie" ]
|
||||||
|
owords = [ "Office" ] * 50 + [ "Outhouse", "Outpost" ]
|
||||||
|
|
||||||
|
cword = random.choice(cwords)
|
||||||
|
eword = random.choice(ewords)
|
||||||
|
oword = random.choice(owords)
|
||||||
|
|
||||||
|
return "%s %s %s" % (cword, eword, oword)
|
||||||
|
|
||||||
|
def menu_items(items):
|
||||||
|
return [ urwid.AttrWrap( ButtonText( cb, txt ), 'menu', 'selected') for (txt, cb) in items ]
|
||||||
|
|
||||||
|
def main_menu():
|
||||||
|
menu = [
|
||||||
|
("New Member", new_member),
|
||||||
|
("Renew Membership", renew_member),
|
||||||
|
("Display Member", display_member),
|
||||||
|
("Search", search_members),
|
||||||
|
("Exit", raise_abort),
|
||||||
|
]
|
||||||
|
|
||||||
|
listbox = urwid.ListBox( menu_items( menu ) )
|
||||||
|
return listbox
|
||||||
|
|
||||||
|
def push_wizard(name, pages, dimensions=(50, 10)):
|
||||||
|
state = {}
|
||||||
|
wiz = Wizard()
|
||||||
|
for page in pages:
|
||||||
|
wiz.add_panel( page(state) )
|
||||||
|
push_window( urwid.Filler( urwid.Padding(
|
||||||
|
urwid.LineBox(wiz), 'center', dimensions[0]),
|
||||||
|
'middle', dimensions[1] ), name )
|
||||||
|
|
||||||
|
def new_member(*args, **kwargs):
|
||||||
|
push_wizard("New Member", [
|
||||||
|
newmember.IntroPage,
|
||||||
|
newmember.InfoPage,
|
||||||
|
newmember.SignPage,
|
||||||
|
newmember.PassPage,
|
||||||
|
newmember.EndPage,
|
||||||
|
])
|
||||||
|
|
||||||
|
def renew_member(*args, **kwargs):
|
||||||
|
push_wizard("Renew Membership", [
|
||||||
|
renew.IntroPage,
|
||||||
|
renew.UserPage,
|
||||||
|
renew.TermPage,
|
||||||
|
renew.PayPage,
|
||||||
|
renew.EndPage,
|
||||||
|
])
|
||||||
|
|
||||||
|
def display_member(a):
|
||||||
|
push_wizard("Display Member", [
|
||||||
|
renew.UserPage,
|
||||||
|
info.InfoPage,
|
||||||
|
], (60, 15))
|
||||||
|
|
||||||
|
def search_members(a):
|
||||||
|
menu = [
|
||||||
|
("Members by term", search_term),
|
||||||
|
("Members by name", search_name),
|
||||||
|
("Back", raise_back),
|
||||||
|
]
|
||||||
|
|
||||||
|
listbox = urwid.ListBox( menu_items( menu ) )
|
||||||
|
push_window(listbox, "Search")
|
||||||
|
|
||||||
|
def search_name(a):
|
||||||
|
push_wizard("By Name", [ search.NamePage ])
|
||||||
|
|
||||||
|
def search_term(a):
|
||||||
|
push_wizard("By Term", [ search.TermPage ])
|
||||||
|
|
||||||
|
def run():
|
||||||
|
push_window( main_menu(), program_name() )
|
||||||
|
event_loop( ui )
|
||||||
|
|
||||||
|
def start():
|
||||||
|
ui.run_wrapper( run )
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
start()
|
|
@ -0,0 +1,134 @@
|
||||||
|
import urwid
|
||||||
|
from csc.apps.urwid.widgets import *
|
||||||
|
from csc.apps.urwid.window import *
|
||||||
|
|
||||||
|
from csc.adm import accounts, members
|
||||||
|
from csc.common.excep import InvalidArgument
|
||||||
|
|
||||||
|
class IntroPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text( "Joining the Computer Science Club" ),
|
||||||
|
urwid.Divider(),
|
||||||
|
urwid.Text( "CSC membership is $2.00 for one term. Please ensure "
|
||||||
|
"the fee is deposited into the safe before continuing." ),
|
||||||
|
]
|
||||||
|
def focusable(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
class InfoPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.userid = WordEdit("UWdir ID: ")
|
||||||
|
self.name = SingleEdit("Full name: ")
|
||||||
|
self.program = SingleEdit("Program of Study: ")
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text( "Member Information - Please Check ID" ),
|
||||||
|
urwid.Divider(),
|
||||||
|
self.userid,
|
||||||
|
self.name,
|
||||||
|
self.program,
|
||||||
|
]
|
||||||
|
def check(self):
|
||||||
|
self.state['userid'] = self.userid.get_edit_text()
|
||||||
|
self.state['name'] = self.name.get_edit_text()
|
||||||
|
self.state['program'] = self.program.get_edit_text()
|
||||||
|
|
||||||
|
if len( self.state['userid'] ) < 4:
|
||||||
|
self.focus_widget( self.userid )
|
||||||
|
set_status("Username is too short")
|
||||||
|
return True
|
||||||
|
elif len( self.state['name'] ) < 4:
|
||||||
|
self.focus_widget( self.name )
|
||||||
|
set_status("Name is too short")
|
||||||
|
return True
|
||||||
|
clear_status()
|
||||||
|
|
||||||
|
class SignPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text( "Machine Usage Policy" ),
|
||||||
|
urwid.Divider(),
|
||||||
|
urwid.Text( "Ensure the new member has signed the "
|
||||||
|
"Machine Usage Policy. Accounts of users who have not "
|
||||||
|
"signed will be suspended if discovered." ),
|
||||||
|
]
|
||||||
|
def focusable(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
class PassPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.password = PassEdit("Password: ")
|
||||||
|
self.pwcheck = PassEdit("Re-enter: ")
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text( "Member Password" ),
|
||||||
|
urwid.Divider(),
|
||||||
|
self.password,
|
||||||
|
self.pwcheck,
|
||||||
|
]
|
||||||
|
def focus_widget(self, widget):
|
||||||
|
self.box.set_focus( self.widgets.index( widget ) )
|
||||||
|
def clear_password(self):
|
||||||
|
self.focus_widget( self.password )
|
||||||
|
self.password.set_edit_text("")
|
||||||
|
self.pwcheck.set_edit_text("")
|
||||||
|
def check(self):
|
||||||
|
self.state['password'] = self.password.get_edit_text()
|
||||||
|
pwcheck = self.pwcheck.get_edit_text()
|
||||||
|
|
||||||
|
if self.state['password'] != pwcheck:
|
||||||
|
self.clear_password()
|
||||||
|
set_status("Passwords do not match")
|
||||||
|
return True
|
||||||
|
elif len(self.state['password']) < 5:
|
||||||
|
self.clear_password()
|
||||||
|
set_status("Password is too short")
|
||||||
|
return True
|
||||||
|
clear_status()
|
||||||
|
|
||||||
|
class EndPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.headtext = urwid.Text("")
|
||||||
|
self.midtext = urwid.Text("")
|
||||||
|
self.widgets = [
|
||||||
|
self.headtext,
|
||||||
|
urwid.Divider(),
|
||||||
|
self.midtext,
|
||||||
|
]
|
||||||
|
def focusable(self):
|
||||||
|
return False
|
||||||
|
def check(self):
|
||||||
|
pop_window()
|
||||||
|
def activate(self):
|
||||||
|
try:
|
||||||
|
if not members.connected(): members.connect()
|
||||||
|
members.new( self.state['userid'], self.state['name'], self.state['program'] )
|
||||||
|
problem = None
|
||||||
|
except members.InvalidRealName:
|
||||||
|
problem = "Invalid real name"
|
||||||
|
except InvalidArgument, e:
|
||||||
|
if e.argname == 'uid' and e.explanation == 'duplicate uid':
|
||||||
|
problem = 'Duplicate userid'
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
if not problem:
|
||||||
|
try:
|
||||||
|
if not accounts.connected(): accounts.connect()
|
||||||
|
accounts.create_member( self.state['userid'], self.state['password'], self.state['name'] )
|
||||||
|
except accounts.NameConflict, e:
|
||||||
|
problem = str(e)
|
||||||
|
except accounts.NoAvailableIDs, e:
|
||||||
|
problem = str(e)
|
||||||
|
except accounts.InvalidArgument, e:
|
||||||
|
problem = str(e)
|
||||||
|
except accounts.LDAPException, e:
|
||||||
|
problem = str(e)
|
||||||
|
except accounts.KrbException, e:
|
||||||
|
problem = str(e)
|
||||||
|
if problem:
|
||||||
|
self.headtext.set_text("Failed to add member")
|
||||||
|
self.midtext.set_text("The error was: '%s'" % problem)
|
||||||
|
else:
|
||||||
|
self.headtext.set_text("Member Added")
|
||||||
|
self.midtext.set_text("Congratulations, %s has been added "
|
||||||
|
"successfully. Please run 'addhomedir %s'."
|
||||||
|
% (self.state['userid'], self.state['userid']))
|
|
@ -0,0 +1,118 @@
|
||||||
|
import urwid
|
||||||
|
|
||||||
|
from csc.apps.urwid.widgets import *
|
||||||
|
from csc.apps.urwid.window import *
|
||||||
|
|
||||||
|
from csc.adm import members, terms
|
||||||
|
|
||||||
|
class IntroPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text( "Renewing Membership" ),
|
||||||
|
urwid.Divider(),
|
||||||
|
urwid.Text( "CSC membership is $2.00 per term. You may pre-register "
|
||||||
|
"for future terms if desired." )
|
||||||
|
]
|
||||||
|
def focusable(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
class UserPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.userid = WordEdit("Username: ")
|
||||||
|
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text( "Member Information" ),
|
||||||
|
urwid.Divider(),
|
||||||
|
self.userid,
|
||||||
|
]
|
||||||
|
def check(self):
|
||||||
|
self.state['userid'] = self.userid.get_edit_text()
|
||||||
|
self.state['member'] = None
|
||||||
|
if self.state['userid']:
|
||||||
|
if not members.connected(): members.connect()
|
||||||
|
self.state['member'] = members.get(self.userid.get_edit_text())
|
||||||
|
if not self.state['member']:
|
||||||
|
set_status("Member not found")
|
||||||
|
self.focus_widget(self.userid)
|
||||||
|
return True
|
||||||
|
|
||||||
|
class TermPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.start = SingleEdit("Start: ")
|
||||||
|
self.count = SingleIntEdit("Count: ")
|
||||||
|
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text( "Terms to Register" ),
|
||||||
|
urwid.Divider(),
|
||||||
|
self.start,
|
||||||
|
self.count,
|
||||||
|
]
|
||||||
|
def activate(self):
|
||||||
|
if not self.start.get_edit_text():
|
||||||
|
old_terms = []
|
||||||
|
if 'term' in self.state['member']:
|
||||||
|
old_terms = self.state['member']['term']
|
||||||
|
self.start.set_edit_text( terms.next_unregistered( old_terms ) )
|
||||||
|
self.count.set_edit_text( "1" )
|
||||||
|
def check(self):
|
||||||
|
try:
|
||||||
|
self.state['terms'] = terms.interval( self.start.get_edit_text(), self.count.value() )
|
||||||
|
except e:
|
||||||
|
self.focus_widget( self.start )
|
||||||
|
set_status( "Invalid start term" )
|
||||||
|
return True
|
||||||
|
for term in self.state['terms']:
|
||||||
|
if members.registered( self.state['userid'], term):
|
||||||
|
self.focus_widget( self.start )
|
||||||
|
set_status( "Already registered for " + term )
|
||||||
|
return True
|
||||||
|
if len(self.state['terms']) == 0:
|
||||||
|
self.focus_widget(self.count)
|
||||||
|
set_status( "Registering for zero terms?" )
|
||||||
|
return True
|
||||||
|
|
||||||
|
class PayPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.midtext = urwid.Text("")
|
||||||
|
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text("Membership Fee"),
|
||||||
|
urwid.Divider(),
|
||||||
|
self.midtext,
|
||||||
|
]
|
||||||
|
def focusable(self):
|
||||||
|
return False
|
||||||
|
def activate(self):
|
||||||
|
regterms = self.state['terms']
|
||||||
|
plural = "term"
|
||||||
|
if len(self.state['terms']) > 1:
|
||||||
|
plural = "terms"
|
||||||
|
self.midtext.set_text("You are registering for %d %s, and owe the "
|
||||||
|
"Computer Science Club $%d.00 in membership fees. "
|
||||||
|
"Please deposit the money in the safe before "
|
||||||
|
"continuing. " % ( len(regterms), plural, len(regterms * 2)))
|
||||||
|
|
||||||
|
class EndPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.headtext = urwid.Text("")
|
||||||
|
self.midtext = urwid.Text("")
|
||||||
|
|
||||||
|
self.widgets = [
|
||||||
|
self.headtext,
|
||||||
|
urwid.Divider(),
|
||||||
|
self.midtext,
|
||||||
|
]
|
||||||
|
def focusable(self):
|
||||||
|
return False
|
||||||
|
def activate(self):
|
||||||
|
try:
|
||||||
|
members.register( self.state['userid'], self.state['terms'] )
|
||||||
|
self.headtext.set_text("Registration Succeeded")
|
||||||
|
self.midtext.set_text("The member has been registered for the following "
|
||||||
|
"terms: " + ", ".join(self.state['terms']) + ".")
|
||||||
|
except Exception, e:
|
||||||
|
self.headtext.set_text("Failed to Register")
|
||||||
|
self.midtext.set_text("You may refund any fees paid or retry."
|
||||||
|
"The error was: '%s'" % e)
|
||||||
|
def check(self):
|
||||||
|
pop_window()
|
|
@ -0,0 +1,69 @@
|
||||||
|
import urwid
|
||||||
|
|
||||||
|
from csc.apps.urwid.widgets import *
|
||||||
|
from csc.apps.urwid.window import *
|
||||||
|
|
||||||
|
from csc.adm import accounts, members, terms
|
||||||
|
from csc.common.excep import InvalidArgument
|
||||||
|
|
||||||
|
class TermPage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.term = SingleEdit("Term: ")
|
||||||
|
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text( "Terms Members" ),
|
||||||
|
urwid.Divider(),
|
||||||
|
self.term,
|
||||||
|
]
|
||||||
|
def check(self):
|
||||||
|
if not members.connected(): members.connect()
|
||||||
|
try:
|
||||||
|
self.state['term'] = self.term.get_edit_text()
|
||||||
|
terms.parse( self.state['term'] )
|
||||||
|
except:
|
||||||
|
self.focus_widget( self.term )
|
||||||
|
set_status( "Invalid term" )
|
||||||
|
return True
|
||||||
|
mlist = members.list_term( self.state['term'] ).values()
|
||||||
|
pop_window()
|
||||||
|
member_list( mlist )
|
||||||
|
|
||||||
|
class NamePage(WizardPanel):
|
||||||
|
def init_widgets(self):
|
||||||
|
self.name = SingleEdit("Name: ")
|
||||||
|
|
||||||
|
self.widgets = [
|
||||||
|
urwid.Text( "Members by Name" ),
|
||||||
|
urwid.Divider(),
|
||||||
|
self.name,
|
||||||
|
]
|
||||||
|
def check(self):
|
||||||
|
if not members.connected(): members.connect()
|
||||||
|
self.state['name'] = self.name.get_edit_text()
|
||||||
|
if not self.state['name']:
|
||||||
|
self.focus_widget( self.term )
|
||||||
|
set_status( "Invalid name" )
|
||||||
|
return True
|
||||||
|
mlist = members.list_name( self.state['name'] ).values()
|
||||||
|
pop_window()
|
||||||
|
member_list( mlist )
|
||||||
|
|
||||||
|
def member_list(mlist):
|
||||||
|
mlist = list(mlist)
|
||||||
|
mlist.sort( lambda x, y: cmp(x['uid'], y['uid']) )
|
||||||
|
buf = ''
|
||||||
|
for member in mlist:
|
||||||
|
if 'uid' in member:
|
||||||
|
uid = member['uid'][0]
|
||||||
|
else:
|
||||||
|
uid = None
|
||||||
|
if 'program' in member:
|
||||||
|
program = member['program'][0]
|
||||||
|
else:
|
||||||
|
program = None
|
||||||
|
attrs = ( uid, member['cn'][0], program )
|
||||||
|
buf += "%10s %30s\n%41s\n\n" % attrs
|
||||||
|
set_status("Press escape to return to the menu")
|
||||||
|
push_window(urwid.ListBox([urwid.Text(buf)]))
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
import urwid
|
||||||
|
|
||||||
|
class ButtonText(urwid.Text):
|
||||||
|
def __init__(self, callback, *args, **kwargs):
|
||||||
|
self.callback = callback
|
||||||
|
urwid.Text.__init__(self, *args, **kwargs)
|
||||||
|
def selectable(self):
|
||||||
|
return True
|
||||||
|
def keypress(self, size, key):
|
||||||
|
if key == 'enter':
|
||||||
|
self.callback(self.get_text())
|
||||||
|
else:
|
||||||
|
return key
|
||||||
|
|
||||||
|
class SingleEdit(urwid.Edit):
|
||||||
|
def keypress(self, size, key):
|
||||||
|
if key == 'enter':
|
||||||
|
return urwid.Edit.keypress(self, size, 'down')
|
||||||
|
else:
|
||||||
|
return urwid.Edit.keypress(self, size, key)
|
||||||
|
|
||||||
|
class SingleIntEdit(urwid.IntEdit):
|
||||||
|
def keypress(self, size, key):
|
||||||
|
if key == 'enter':
|
||||||
|
return urwid.Edit.keypress(self, size, 'down')
|
||||||
|
else:
|
||||||
|
return urwid.Edit.keypress(self, size, key)
|
||||||
|
|
||||||
|
class WordEdit(SingleEdit):
|
||||||
|
def valid_char(self, ch):
|
||||||
|
return urwid.Edit.valid_char(self, ch) and ch != ' '
|
||||||
|
|
||||||
|
class PassEdit(SingleEdit):
|
||||||
|
def get_text(self):
|
||||||
|
text = urwid.Edit.get_text(self)
|
||||||
|
return (self.caption + " " * len(self.get_edit_text()), text[1])
|
||||||
|
|
||||||
|
class Wizard(urwid.WidgetWrap):
|
||||||
|
def __init__(self):
|
||||||
|
self.selected = None
|
||||||
|
self.panels = []
|
||||||
|
|
||||||
|
self.panelwrap = urwid.WidgetWrap( urwid.SolidFill() )
|
||||||
|
self.back = urwid.Button("Back", self.back)
|
||||||
|
self.next = urwid.Button("Next", self.next)
|
||||||
|
self.buttons = urwid.Columns( [ self.back, self.next ], dividechars=3, focus_column=1 )
|
||||||
|
pad = urwid.Padding( self.buttons, ('fixed right', 2), 19 )
|
||||||
|
self.pile = urwid.Pile( [self.panelwrap, ('flow', pad)], 0 )
|
||||||
|
urwid.WidgetWrap.__init__(self, self.pile)
|
||||||
|
|
||||||
|
def add_panel(self, panel):
|
||||||
|
self.panels.append( panel )
|
||||||
|
if len(self.panels) == 1:
|
||||||
|
self.select(0)
|
||||||
|
|
||||||
|
def select(self, panelno, set_focus=True):
|
||||||
|
if 0 <= panelno < len(self.panels):
|
||||||
|
self.selected = panelno
|
||||||
|
self.panelwrap.set_w( self.panels[panelno] )
|
||||||
|
self.panels[panelno].activate()
|
||||||
|
|
||||||
|
if set_focus:
|
||||||
|
if self.panels[panelno].focusable():
|
||||||
|
self.pile.set_focus( 0 )
|
||||||
|
else:
|
||||||
|
self.pile.set_focus( 1 )
|
||||||
|
|
||||||
|
def next(self, *args, **kwargs):
|
||||||
|
if self.panels[self.selected].check():
|
||||||
|
self.select( self.selected )
|
||||||
|
return
|
||||||
|
self.select(self.selected + 1)
|
||||||
|
|
||||||
|
def back(self, *args, **kwargs):
|
||||||
|
self.select(self.selected - 1, False)
|
||||||
|
|
||||||
|
class WizardPanel(urwid.WidgetWrap):
|
||||||
|
def __init__(self, state):
|
||||||
|
self.state = state
|
||||||
|
self.init_widgets()
|
||||||
|
self.box = urwid.ListBox( urwid.SimpleListWalker( self.widgets ) )
|
||||||
|
urwid.WidgetWrap.__init__( self, self.box )
|
||||||
|
def init_widgets(self):
|
||||||
|
self.widgets = []
|
||||||
|
def focus_widget(self, widget):
|
||||||
|
self.box.set_focus( self.widgets.index( widget ) )
|
||||||
|
def focusable(self):
|
||||||
|
return True
|
||||||
|
def check(self):
|
||||||
|
return
|
||||||
|
def activate(self):
|
||||||
|
return
|
|
@ -0,0 +1,65 @@
|
||||||
|
import urwid
|
||||||
|
|
||||||
|
window_stack = []
|
||||||
|
window_names = []
|
||||||
|
|
||||||
|
header = urwid.Text( "" )
|
||||||
|
footer = urwid.Text( "" )
|
||||||
|
top = urwid.Frame( urwid.SolidFill(), header, footer )
|
||||||
|
|
||||||
|
def push_window( frame, name=None ):
|
||||||
|
window_stack.append( frame )
|
||||||
|
window_names.append( name )
|
||||||
|
update_top()
|
||||||
|
|
||||||
|
def pop_window():
|
||||||
|
if len(window_stack) == 1:
|
||||||
|
return False
|
||||||
|
window_stack.pop()
|
||||||
|
window_names.pop()
|
||||||
|
update_top()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def update_top():
|
||||||
|
names = [ n for n in window_names if n ]
|
||||||
|
header.set_text(" - ".join( names ) + "\n")
|
||||||
|
top.set_body( window_stack[-1] )
|
||||||
|
|
||||||
|
def set_status(message):
|
||||||
|
footer.set_text(message)
|
||||||
|
|
||||||
|
def clear_status():
|
||||||
|
footer.set_text("")
|
||||||
|
|
||||||
|
class Abort(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Back(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def raise_abort(*args, **kwargs):
|
||||||
|
raise Abort()
|
||||||
|
|
||||||
|
def raise_back(*args, **kwarg):
|
||||||
|
raise Back()
|
||||||
|
|
||||||
|
def event_loop(ui):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
cols, rows = ui.get_cols_rows()
|
||||||
|
canvas = top.render( (cols, rows), focus=True )
|
||||||
|
ui.draw_screen( (cols, rows), canvas )
|
||||||
|
|
||||||
|
keys = ui.get_input()
|
||||||
|
for k in keys:
|
||||||
|
if k == "esc":
|
||||||
|
if not pop_window():
|
||||||
|
break
|
||||||
|
elif k == "window resize":
|
||||||
|
(cols, rows) = ui.get_cols_rows()
|
||||||
|
else:
|
||||||
|
top.keypress( (cols, rows), k )
|
||||||
|
except Back:
|
||||||
|
pop_window()
|
||||||
|
except (Abort, KeyboardInterrupt):
|
||||||
|
return
|
Loading…
Reference in New Issue