From 34b2aa1028bd06fcbc98cd2a0f61bfeeca1565a5 Mon Sep 17 00:00:00 2001 From: Michael Spang Date: Thu, 20 Dec 2007 16:43:33 -0500 Subject: [PATCH] Add club representative support The only difference between a club representative account and a member account is how they are registered for terms. If you are already a representative for the current term, you need only go through the membership renewal process to become a full fledged member. --- ceo/members.py | 35 +++++++++++++++++++++++++++++ ceo/urwid/main.py | 21 ++++++++++++++++- ceo/urwid/newmember.py | 30 +++++++++++++++++++------ ceo/urwid/renew.py | 51 +++++++++++++++++++++++++++++++++++------- etc/csc.schema | 6 ++++- 5 files changed, 126 insertions(+), 17 deletions(-) diff --git a/ceo/members.py b/ceo/members.py index 25bd0d1..83890e8 100644 --- a/ceo/members.py +++ b/ceo/members.py @@ -426,6 +426,41 @@ def register(userid, term_list): ld.modify_s(user_dn, mlist) +def register_nonmember(userid, term_list): + """Registers a non-member for one or more terms.""" + + user_dn = 'uid=%s,%s' % (ldapi.escape(userid), cfg['users_base']) + + if type(term_list) in (str, unicode): + term_list = [ term_list ] + + ldap_member = get(userid) + if not ldap_member: + raise NoSuchMember(userid) + + if 'term' not in ldap_member: + ldap_member['term'] = [] + if 'nonMemberTerm' not in ldap_member: + ldap_member['nonMemberTerm'] = [] + + new_member = ldap_member.copy() + new_member['nonMemberTerm'] = new_member['nonMemberTerm'][:] + + for term in term_list: + + # check term syntax + if not re.match('^[wsf][0-9]{4}$', term): + raise InvalidTerm(term) + + # add the term to the entry + if not term in ldap_member['nonMemberTerm'] \ + and not term in ldap_member['term']: + new_member['nonMemberTerm'].append(term) + + mlist = ldapi.make_modlist(ldap_member, new_member) + ld.modify_s(user_dn, mlist) + + def registered(userid, term): """ Determines whether a member is registered diff --git a/ceo/urwid/main.py b/ceo/urwid/main.py index 44d244d..9d26d28 100644 --- a/ceo/urwid/main.py +++ b/ceo/urwid/main.py @@ -55,8 +55,10 @@ syscom_data = { def main_menu(): menu = [ ("New Member", new_member, None), + ("New Club Rep", new_club_user, None), ("Renew Membership", renew_member, None), - ("Create Club Account", new_club, None), + ("Renew Club Rep", renew_club_user, None), + ("New Club", new_club, None), ("Display Member", display_member, None), ("Change Shell", change_shell, None), ("Search", search_members, None), @@ -86,6 +88,15 @@ def new_club(*args, **kwargs): (newmember.EndPage, "club"), ], (60, 15)) +def new_club_user(*args, **kwargs): + push_wizard("New Club Rep Account", [ + newmember.ClubUserIntroPage, + newmember.InfoPage, + newmember.SignPage, + newmember.PassPage, + (newmember.EndPage, "clubuser"), + ], (60, 15)) + def manage_group(*args, **kwargs): push_wizard("Manage Club or Group Members", [ groups.IntroPage, @@ -101,6 +112,14 @@ def renew_member(*args, **kwargs): renew.EndPage, ]) +def renew_club_user(*args, **kwargs): + push_wizard("Renew Club Rep Account", [ + renew.ClubUserIntroPage, + renew.UserPage, + (renew.TermPage, "clubuser"), + (renew.EndPage, "clubuser"), + ], (60, 15)) + def display_member(data): push_wizard("Display Member", [ renew.UserPage, diff --git a/ceo/urwid/newmember.py b/ceo/urwid/newmember.py index 4fc3bc5..e8bcbea 100644 --- a/ceo/urwid/newmember.py +++ b/ceo/urwid/newmember.py @@ -17,19 +17,32 @@ class IntroPage(WizardPanel): class ClubIntroPage(WizardPanel): def init_widgets(self): self.widgets = [ - urwid.Text( "Club Services" ), + urwid.Text( "Club Accounts" ), urwid.Divider(), urwid.Text( "We provide other UW clubs accounts for email and " "web hosting, free of charge. Like members, clubs " "get web hosting at %s. We can also arrange for " "uwaterloo.ca subdomains; please instruct the club " "representative to contact the systems committee " - "for more information." - "\n\nNote: This is not complete. Authorizing members " - "to access the club account still requires manual " - "intervention." - % "http://csclub.uwaterloo.ca/~clubid/" - ) + "for more information. Club accounts do not have " + "passwords, and exist primarily to own club data. " + % "http://csclub.uwaterloo.ca/~clubid/" ), + ] + def focusable(self): + return False + +class ClubUserIntroPage(WizardPanel): + def init_widgets(self): + self.widgets = [ + urwid.Text( "Club Rep Account" ), + urwid.Divider(), + urwid.Text( "This is for people who need access to a club account, " + "but are not currently interested in full CSC membership. " + "Registering a user in this way grants one term of free " + "access to our machines, without any other membership " + "privileges (they can't vote, hold office, etc). If such " + "a user later decides to join, use the Renew Membership " + "option." ), ] def focusable(self): return False @@ -150,6 +163,9 @@ class EndPage(WizardPanel): if self.utype == 'member': members.create_member( self.state['userid'], self.state['password'], self.state['name'], self.state['program'] ) members.register( self.state['userid'], terms.current() ) + elif self.utype == 'clubuser': + members.create_member( self.state['userid'], self.state['password'], self.state['name'], self.state['program'] ) + members.register_nonmember( self.state['userid'], terms.current() ) elif self.utype == 'club': members.create_club( self.state['userid'], self.state['name'] ) else: diff --git a/ceo/urwid/renew.py b/ceo/urwid/renew.py index b9abb7c..870d3fb 100644 --- a/ceo/urwid/renew.py +++ b/ceo/urwid/renew.py @@ -14,6 +14,21 @@ class IntroPage(WizardPanel): def focusable(self): return False +class ClubUserIntroPage(IntroPage): + def init_widgets(self): + self.widgets = [ + urwid.Text( "Renewing Club User Account" ), + urwid.Divider(), + urwid.Text( "In order for clubs to maintain websites hosted by " + "the Computer Science Club, they need access to our " + "machines. We grant accounts to club users at no charge " + "in order to provide this access. Registering a user " + "in this way grants one term of free access to our " + "machines, without any other membership privileges " + "(they can't vote, hold office, etc). If such a user " + "decides to join, use the Renew Membership option." ) + ] + class UserPage(WizardPanel): def init_widgets(self): self.userid = LdapWordEdit(csclub_uri, csclub_base, 'uid', @@ -35,6 +50,9 @@ class UserPage(WizardPanel): return True class TermPage(WizardPanel): + def __init__(self, state, utype='member'): + self.utype = utype + WizardPanel.__init__(self, state) def init_widgets(self): self.start = SingleEdit("Start: ") self.count = SingleIntEdit("Count: ") @@ -47,10 +65,14 @@ class TermPage(WizardPanel): ] 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.terms = self.state['member'].get('term', []) + self.nmterms = self.state['member'].get('nonMemberTerm', []) + + if self.utype == 'member': + self.start.set_edit_text( terms.next_unregistered( self.terms ) ) + else: + self.start.set_edit_text( terms.next_unregistered( self.terms + self.nmterms ) ) + self.count.set_edit_text( "1" ) def check(self): try: @@ -60,7 +82,12 @@ class TermPage(WizardPanel): set_status( "Invalid start term" ) return True for term in self.state['terms']: - if members.registered( self.state['userid'], term): + if self.utype == 'member': + already = term in self.terms + else: + already = term in self.terms or term in self.nmterms + + if already: self.focus_widget( self.start ) set_status( "Already registered for " + term ) return True @@ -91,6 +118,9 @@ class PayPage(WizardPanel): "continuing. " % ( len(regterms), plural, len(regterms * 2))) class EndPage(WizardPanel): + def __init__(self, state, utype='member'): + self.utype = utype + WizardPanel.__init__(self, state) def init_widgets(self): self.headtext = urwid.Text("") self.midtext = urwid.Text("") @@ -105,10 +135,15 @@ class EndPage(WizardPanel): def activate(self): problem = None 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']) + ".") + if self.utype == 'member': + members.register( self.state['userid'], self.state['terms'] ) + self.midtext.set_text("The member has been registered for the following " + "terms: " + ", ".join(self.state['terms']) + ".") + else: + members.register_nonmember( self.state['userid'], self.state['terms'] ) + self.midtext.set_text("The club user has been registered for the following " + "terms: " + ", ".join(self.state['terms']) + ".") except ldap.LDAPError, e: problem = ldapi.format_ldaperror(e) except members.MemberException, e: diff --git a/etc/csc.schema b/etc/csc.schema index 13c24e2..e104398 100644 --- a/etc/csc.schema +++ b/etc/csc.schema @@ -16,10 +16,14 @@ attributetype ( 1.3.6.1.4.1.27934.1.1.4 NAME 'position' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{32} ) +attributetype ( 1.3.6.1.4.1.27934.1.1.5 NAME 'nonMemberTerm' + EQUALITY caseIgnoreIA5Match + SYNTAX 1.3.6.1.4.1.1466.115.121.1.26{5} ) + objectclass ( 1.3.6.1.4.1.27934.1.2.1 NAME 'member' SUP top AUXILIARY MUST ( cn $ uid ) - MAY ( studentid $ program $ term $ description $ position ) ) + MAY ( studentid $ program $ term $ nonMemberTerm $ description $ position ) ) objectclass ( 1.3.6.1.4.1.27934.1.2.2 NAME 'club' SUP top AUXILIARY