Split user/group into separate files, convert to genshi for templates. TODO: Form validation

This commit is contained in:
Ricky Zhou (周家杰) 2007-08-16 13:40:01 -07:00
parent f62a04b427
commit a1283c3a1c
36 changed files with 1031 additions and 765 deletions

52
fas/fas/auth.py Normal file
View file

@ -0,0 +1,52 @@
from turbogears import config
from fas.fasLDAP import UserAccount
from fas.fasLDAP import Person
from fas.fasLDAP import Groups
from fas.fasLDAP import UserGroup
ADMINGROUP = config.get('admingroup')
def isAdmin(userName, g=None):
if not g:
g = Groups.byUserName(userName)
try:
g[ADMINGROUP].cn
return True
except KeyError:
return False
def canAdminGroup(userName, groupName, g=None):
# TODO: Allow the group owner to admin a group.
if not g:
g = Groups.byUserName(userName)
try:
if isAdmin(userName, g) or \
(g[groupName].fedoraRoleType.lower() == 'administrator'):
return True
else:
return False
except:
return False
def canSponsorGroup(userName, groupName, g=None):
if not g:
g = Groups.byUserName(userName)
try:
if isAdmin(userName, g) or \
canAdminGroup(userName, groupName, g) or \
(g[groupName].fedoraRoleType.lower() == 'sponsor'):
return True
else:
return False
except:
return False
def canEditUser(userName, editUserName):
if userName == editUserName:
return True
elif isAdmin(userName):
return True
else:
return False

View file

@ -13,6 +13,7 @@ admingroup = 'accounts'
# which view (template engine) to use if one is not specified in the
# template name
# tg.defaultview = "kid"
tg.defaultview = "genshi"
# The following kid settings determine the settings used by the kid serializer.

View file

@ -10,106 +10,34 @@ from turbogears import exception_handler
import turbogears
import ldap
import time
from operator import itemgetter
from fas.user import User
from fas.group import Group
# from fas import json
# import logging
# log = logging.getLogger("fas.controllers")
ADMINGROUP = config.get('admingroup')
class knownUser(validators.FancyValidator):
def _to_python(self, value, state):
return value.strip()
def validate_python(self, value, state):
p = Person.byUserName(value)
if p.cn:
raise validators.Invalid(_("'%s' already exists") % value, value, state)
class unknownUser(validators.FancyValidator):
def _to_python(self, value, state):
return value.strip()
def validate_python(self, value, state):
p = Person.byUserName(value)
if not p.cn:
raise validators.Invalid(_("'%s' does not exist") % value, value, state)
class unknownGroup(validators.FancyValidator):
def _to_python(self, value, state):
return value.strip()
def validate_python(self, value, state):
g = Groups.groups(groupName)
if not g:
raise validators.Invalid(_("'%s' does not exist") % value, value, state)
class newPerson(widgets.WidgetsList):
# cn = widgets.TextField(label='Username', validator=validators.PlainText(not_empty=True, max=10))
cn = widgets.TextField(label=_('Username'), validator=validators.All(knownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
givenName = widgets.TextField(label=_('Full Name'), validator=validators.String(not_empty=True, max=42))
mail = widgets.TextField(label=_('Email'), validator=validators.Email(not_empty=True, strip=True))
telephoneNumber = widgets.TextField(label=_('Telephone Number'), validator=validators.PhoneNumber(not_empty=True))
postalAddress = widgets.TextArea(label=_('Postal Address'), validator=validators.NotEmpty)
newPersonForm = widgets.ListForm(fields=newPerson(), submit_text=_('Sign Up'))
class editPerson(widgets.WidgetsList):
# cn = widgets.TextField(label='Username', validator=validators.PlainText(not_empty=True, max=10))
userName = widgets.HiddenField(validator=validators.All(unknownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
givenName = widgets.TextField(label=_('Full Name'), validator=validators.String(not_empty=True, max=42))
mail = widgets.TextField(label=_('Email'), validator=validators.Email(not_empty=True, strip=True))
fedoraPersonBugzillaMail = widgets.TextField(label=_('Bugzilla Email'), validator=validators.Email(not_empty=True, strip=True))
fedoraPersonIrcNick = widgets.TextField(label=_('IRC Nick'))
fedoraPersonKeyId = widgets.TextField(label=_('PGP Key'))
telephoneNumber = widgets.TextField(label=_('Telephone Number'), validator=validators.PhoneNumber(not_empty=True))
postalAddress = widgets.TextArea(label=_('Postal Address'), validator=validators.NotEmpty)
description = widgets.TextArea(label=_('Description'))
editPersonForm = widgets.ListForm(fields=editPerson(), submit_text=_('Update'))
class editGroup(widgets.WidgetsList):
groupName = widgets.HiddenField(validator=validators.All(unknownGroup(not_empty=True, max=10), validators.String(max=32, min=3)))
fedoraGroupDesc = widgets.TextField(label=_('Description'), validator=validators.NotEmpty)
fedoraGroupOwner = widgets.TextField(label=_('Group Owner'), validator=validators.All(knownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
fedoraGroupNeedsSponsor = widgets.CheckBox(label=_('Needs Sponsor'))
fedoraGroupUserCanRemove = widgets.CheckBox(label=_('Self Removal'))
fedoraGroupJoinMsg = widgets.TextField(label=_('Group Join Message'))
editGroupForm = widgets.ListForm(fields=editGroup(), submit_text=_('Update'))
class findUser(widgets.WidgetsList):
userName = widgets.AutoCompleteField(label=_('Username'), search_controller='search', search_param='userName', result_name='people')
action = widgets.HiddenField(default='apply', validator=validators.String(not_empty=True))
groupName = widgets.HiddenField(validator=validators.String(not_empty=True))
searchUserForm = widgets.ListForm(fields=findUser(), submit_text=_('Invite'))
class Root(controllers.RootController):
@expose(template="fas.templates.error")
def errorMessage(self, tg_exceptions=None):
''' Generic exception handler'''
# Maybe add a popup or alert or some damn thing.
message = '%s' % tg_exceptions
return dict(handling_value=True,exception=message)
user = User()
group = Group()
@expose(template="fas.templates.welcome")
# @identity.require(identity.in_group("admin"))
def index(self):
# log.debug("Happy TurboGears Controller Responding For Duty")
if turbogears.identity.not_anonymous():
turbogears.redirect('home')
return dict(now=time.ctime())
@expose(template="fas.templates.home")
@identity.require(identity.not_anonymous())
def home(self):
from feeds import Koji
builds = Koji(turbogears.identity.current.user_name)
return dict(builds=builds)
@expose(template="fas.templates.dump", format="plain", content_type="text/plain")
def groupDump(self, groupName=None):
groups = Groups.byGroupName(groupName)
return dict(groups=groups, Person=Person)
@expose(template="fas.templates.login")
def login(self, forward_url=None, previous_url=None, *args, **kw):
@ -143,408 +71,23 @@ class Root(controllers.RootController):
turbogears.flash(_('You have successfully logged out.'))
raise redirect("/")
@expose(template="fas.templates.viewAccount")
@identity.require(identity.not_anonymous())
def viewAccount(self,userName=None, action=None):
if not userName:
userName = turbogears.identity.current.user_name
if turbogears.identity.current.user_name == userName:
personal = True
else:
personal = False
try:
Groups.byUserName(turbogears.identity.current.user_name)[ADMINGROUP].cn
admin = True
except KeyError:
admin = False
user = Person.byUserName(userName)
groups = Groups.byUserName(userName)
groupsPending = Groups.byUserName(userName, unapprovedOnly=True)
groupdata={}
for g in groups:
groupdata[g] = Groups.groups(g)[g]
for g in groupsPending:
groupdata[g] = Groups.groups(g)[g]
try:
groups['cla_done']
claDone=True
except KeyError:
claDone=None
return dict(user=user, groups=groups, groupsPending=groupsPending, action=action, groupdata=groupdata, claDone=claDone, personal=personal, admin=admin)
@expose(template="fas.templates.editAccount")
@identity.require(identity.not_anonymous())
def editAccount(self, userName=None, action=None):
if userName:
try:
Groups.byUserName(turbogears.identity.current.user_name)[ADMINGROUP].cn
if not userName:
userName = turbogears.identity.current.user_name
except KeyError:
turbogears.flash(_('You cannot edit %s') % userName )
userName = turbogears.identity.current.user_name
else:
userName = turbogears.identity.current.user_name
user = Person.byUserName(userName)
value = {'userName' : userName,
'givenName' : user.givenName,
'mail' : user.mail,
'fedoraPersonBugzillaMail' : user.fedoraPersonBugzillaMail,
'fedoraPersonIrcNick' : user.fedoraPersonIrcNick,
'fedoraPersonKeyId' : user.fedoraPersonKeyId,
'telephoneNumber' : user.telephoneNumber,
'postalAddress' : user.postalAddress,
'description' : user.description, }
return dict(form=editPersonForm, value=value)
@expose(template="fas.templates.viewGroup")
@exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
@identity.require(identity.not_anonymous())
def viewGroup(self, groupName):
try:
groups = Groups.byGroupName(groupName, includeUnapproved=True)
except KeyError:
raise ValueError, _('Group: %s - Does not exist!') % groupName
try:
group = Groups.groups(groupName)[groupName]
except TypeError:
raise ValueError, _('Group: %s - Does not exist!') % groupName
userName = turbogears.identity.current.user_name
try:
myStatus = groups[userName].fedoraRoleStatus
except KeyError:
# Not in group
myStatus = 'Not a Member' # This has say 'Not a Member'
except TypeError:
groups = {}
try:
me = groups[userName]
except:
me = UserGroup()
#searchUserForm.groupName.display('group')
#findUser.groupName.display(value='fff')
value = {'groupName' : group.cn}
return dict(groups=groups, group=group, me=me, searchUserForm=searchUserForm, value=value)
@expose(template="fas.templates.editGroup")
@identity.require(identity.not_anonymous())
def editGroup(self, groupName, action=None):
userName = turbogears.identity.current.user_name
try:
Groups.byUserName(userName)[ADMINGROUP].cn
except KeyError:
try:
Groups.byUserName(userName)[groupName]
if Groups.byUserName(userName)[groupName].fedoraRoleType.lower() != 'administrator':
raise KeyError
except KeyError:
turbogears.flash(_('You cannot edit %s') % groupName)
turbogears.redirect('viewGroup?groupName=%s' % groupName)
group = Groups.groups(groupName)[groupName]
value = {'groupName' : groupName,
'fedoraGroupOwner' : group.fedoraGroupOwner,
'fedoraGroupType' : group.fedoraGroupType,
'fedoraGroupNeedsSponsor' : (group.fedoraGroupNeedsSponsor.upper() == 'TRUE'),
'fedoraGroupUserCanRemove' : (group.fedoraGroupUserCanRemove.upper() == 'TRUE'),
'fedoraGroupJoinMsg' : group.fedoraGroupJoinMsg,
'fedoraGroupDesc' : group.fedoraGroupDesc, }
#'fedoraGroupRequires' : group.fedoraGroupRequires, }
return dict(form=editGroupForm, value=value)
@expose(template="fas.templates.groupList")
@exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
@identity.require(identity.not_anonymous())
def listGroup(self, search='*'):
groups = Groups.groups(search)
userName = turbogears.identity.current.user_name
myGroups = Groups.byUserName(userName)
try:
groups.keys()
except:
turbogears.flash(_("No Groups found matching '%s'") % search)
groups = {}
return dict(groups=groups, search=search, myGroups=myGroups)
@expose(template="fas.templates.resetPassword")
@exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
def resetPassword(self, userName=None, password=None, passwordCheck=None, mail=None):
import turbomail
# Logged in
if turbogears.identity.current.user_name and not password:
return dict()
# Not logged in
if not (userName and mail) and not turbogears.identity.current.user_name:
# turbogears.flash('Please provide your username and password')
return dict()
if turbogears.identity.current.user_name:
userName = turbogears.identity.current.user_name
p = Person.byUserName(userName)
if password and passwordCheck:
if not password == passwordCheck:
turbogears.flash(_('Passwords do not match!'))
return dict()
if len(password) < 8:
turbogears.flash(_('Password is too short. Must be at least 8 characters long'))
return dict()
newpass = p.generatePassword(password)
if userName and mail and not turbogears.identity.current.user_name:
if not mail == p.mail:
turbogears.flash(_("username + email combo unknown."))
return dict()
newpass = p.generatePassword()
message = turbomail.Message('accounts@fedoraproject.org', p.mail, _('Fedora Project Password Reset'))
message.plain = _("You have requested a password reset! Your new password is - %s \nPlease go to https://admin.fedoraproject.org/fas/ to change it") % newpass['pass']
turbomail.enqueue(message)
p.__setattr__('userPassword', newpass['hash'])
p.userPassword = newpass['hash']
print "PASS: %s" % newpass['pass']
if turbogears.identity.current.user_name:
turbogears.flash(_("Password Changed"))
turbogears.redirect("viewAccount")
else:
turbogears.flash(_('Your password has been emailed to you'))
return dict()
@expose(template="fas.templates.userList")
@exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
@identity.require(identity.in_group("accounts"))
def listUser(self, search='a*'):
users = Person.users(search)
try:
users[0]
except:
turbogears.flash(_("No users found matching '%s'") % search)
users = []
cla_done = Groups.byGroupName('cla_done')
claDone = {}
for u in users:
try:
cla_done[u]
claDone[u] = True
except KeyError:
claDone[u] = False
return dict(users=users, claDone=claDone, search=search)
listUsers = listUser
# @expose(template='fas.templates.apply')
# @exception_handler(errorMessage, rules="isinstance(tg_exceptions,ValueError)")
# @identity.require(identity.not_anonymous())
# def sudo(self, userName):
# # This doesn't work
# turbogears.identity.current.user_name=userName
# turbogears.flash('Sudoed to %s' % userName)
# turbogears.recirect('viewAccount')
# @error_handler(viewGroup)
# @validate(form=newPersonForm)
@expose(template='fas.templates.apply')
@identity.require(identity.not_anonymous())
def modifyGroup(self, groupName, action, userName, **kw):
''' Modifies group based on action, groupName and userName '''
try:
userName = userName['text']
except TypeError:
pass
sponsor = turbogears.identity.current.user_name
try:
group = Groups.groups(groupName)[groupName]
except KeyError:
turbogears.flash(_('Group Error: %s does not exist.') % groupName)
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
try:
p = Person.byUserName(userName)
if not p.cn:
raise KeyError, userName
except KeyError:
turbogears.flash(_('User Error: User %s does not exist.') % userName)
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
g = Groups.byGroupName(groupName, includeUnapproved=True)
# Apply user to a group (as in application)
if action == 'apply':
try:
Groups.apply(groupName, userName)
except ldap.ALREADY_EXISTS:
turbogears.flash(_('%s Already in group!') % p.cn)
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
else:
turbogears.flash(_('%s Applied!') % p.cn)
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
# Some error checking for the sponsors
if g[userName].fedoraRoleType.lower() == 'administrator' and g[sponsor].fedoraRoleType.lower() == 'sponsor':
raise ValueError, _('Sponsors cannot alter administrators. End of story.')
try:
userGroup = Groups.byGroupName(groupName)[userName]
except KeyError:
# User not already in the group (happens when users apply for a group)
userGroup = UserGroup()
pass
# Remove user from a group
if action == 'remove':
try:
Groups.remove(group.cn, p.cn)
except TypeError:
turbogears.flash(_('%(name)s could not be removed from %(group)s!') % {'name' : p.cn, 'group' : group.cn})
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
else:
turbogears.flash(_('%(name)s removed from %(group)s!') % {'name' : p.cn, 'group' : group.cn})
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
return dict()
# Upgrade user in a group
elif action == 'upgrade':
if g[userName].fedoraRoleType.lower() == 'sponsor' and g[sponsor].fedoraRoleType.lower() == 'sponsor':
raise ValueError, _('Sponsors cannot admin other sponsors')
try:
p.upgrade(groupName)
except TypeError, e:
turbogears.flash(_('Cannot upgrade %(name)s - %(error)s!') % {'name' : p.cn, 'error' : e})
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
turbogears.flash(_('%s Upgraded!') % p.cn)
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
# Downgrade user in a group
elif action == 'downgrade':
if g[userName].fedoraRoleType.lower() == 'administrator' and g[sponsor].fedoraRoleType.lower() == 'sponsor':
raise ValueError, _('Sponsors cannot downgrade admins')
try:
p.downgrade(groupName)
except TypeError, e:
turbogears.flash(_('Cannot downgrade %(name)s - %(error)s!') % {'name' : p.cn, 'error' : e})
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
turbogears.flash(_('%s Downgraded!') % p.cn)
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
# Sponsor / Approve User
elif action == 'sponsor' or action == 'apply':
p.sponsor(groupName, sponsor)
turbogears.flash(_('%s has been sponsored!') % p.cn)
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
turbogears.flash(_('Invalid action: %s') % action)
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
return dict()
## TODO: Invitation cleanup- move out and validate!
@expose(template='fas.templates.inviteMember')
@exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
@identity.require(identity.not_anonymous())
def inviteMember(self, name=None, email=None, skills=None):
if name and email:
turbogears.flash(_('Invitation Sent to: "%(name)s" <%(email)s>') % {'name' : name, 'email' : email})
turbogears.flash(_('Invitation Sent to: "%(name)s" <%(email)s>') % {'name': name, 'email': email})
if name or email:#FIXME
turbogears.flash(_('Please provide both an email address and the persons name.'))
return dict()
@expose(template='fas.templates.apply')
@exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
@identity.require(identity.not_anonymous())
def applyForGroup(self, groupName, action=None, requestField=None):
userName = turbogears.identity.current.user_name
group = Groups.groups(groupName)[groupName]
user = Person.byUserName(userName)
if action != 'Remove':
try:
Groups.apply(groupName, userName)
turbogears.flash(_('Application sent for %s') % user.cn)
except ldap.ALREADY_EXISTS, e:
turbogears.flash(_('Application Denied: %s') % e[0]['desc'])
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
if action == 'Remove' and group.fedoraGroupUserCanRemove == 'TRUE':
try:
Groups.remove(group.cn, user.cn)
except TypeError:
turbogears.flash(_('%(user)s could not be removed from %(group)s!') % {'user' : user.cn, 'group' : group.cn})
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
else:
turbogears.flash(_('%(user)s removed from %(group)s!') % {'user' : user.cn, 'group' : group.cn})
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
else:
turbogears.flash(_('%s does not allow self removal') % group.cn)
turbogears.redirect('viewGroup?groupName=%s' % group.cn)
return dict()
@expose(template='fas.templates.signUp')
def signUp(self):
if turbogears.identity.not_anonymous():
turbogears.flash(_('No need to sign up, You have an account!'))
turbogears.redirect('viewAccount')
return dict(form=newPersonForm)
@validate(form=newPersonForm)
@error_handler(signUp)
@expose(template='fas.templates.signUp')
def newAccountSubmit(self, cn, givenName, mail, telephoneNumber, postalAddress):
import turbomail
try:
Person.newPerson(cn.encode('utf8'), givenName.encode('utf8'), mail.encode('utf8'), telephoneNumber.encode('utf8'), postalAddress.encode('utf8'))
p = Person.byUserName(cn.encode('utf8'))
newpass = p.generatePassword()
message = turbomail.Message('accounts@fedoraproject.org', p.mail, _('Fedora Project Password Reset'))
message.plain = _("You have requested a password reset! Your new password is - %s \nPlease go to https://admin.fedoraproject.org/fas/ to change it") % newpass['pass']
turbomail.enqueue(message)
p.__setattr__('userPassword', newpass['hash'])
turbogears.flash(_('Your password has been emailed to you. Please log in with it and change your password'))
turbogears.redirect('/')
except ldap.ALREADY_EXISTS:
turbogears.flash(_('%s Already Exists, Please pick a different name') % cn)
turbogears.redirect('signUp')
return dict()
@validate(form=editPersonForm)
@error_handler(editAccount)
@expose(template='fas.templates.editAccount')
def editAccountSubmit(self, givenName, mail, fedoraPersonBugzillaMail, telephoneNumber, postalAddress, userName=None, fedoraPersonIrcNick='', fedoraPersonKeyId='', description=''):
if userName:
try:
Groups.byUserName(turbogears.identity.current.user_name)[ADMINGROUP].cn
if not userName:
userName = turbogears.identity.current.user_name
except KeyError:
turbogears.flash(_('You cannot view %s') % userName)
userName = turbogears.identity.current.user_name
turbogears.redirect("editAccount")
return dict()
else:
userName = turbogears.identity.current.user_name
user = Person.byUserName(userName)
user.__setattr__('givenName', givenName.encode('utf8'))
user.__setattr__('mail', mail.encode('utf8'))
user.__setattr__('fedoraPersonBugzillaMail', fedoraPersonBugzillaMail.encode('utf8'))
user.__setattr__('fedoraPersonIrcNick', fedoraPersonIrcNick.encode('utf8'))
user.__setattr__('fedoraPersonKeyId', fedoraPersonKeyId.encode('utf8'))
user.__setattr__('telephoneNumber', telephoneNumber.encode('utf8'))
user.__setattr__('postalAddress', postalAddress.encode('utf8'))
user.__setattr__('description', description.encode('utf8'))
turbogears.flash(_('Your account has been updated.'))
turbogears.redirect("viewAccount?userName=%s" % userName)
return dict()
@expose(format="json")
def search(self, userName=None, groupName=None):
people = Person.users('%s*' % userName)
return dict(people=
filter(lambda item: userName in item.lower(), people))
@expose(template='fas.templates.invite')
@exception_handler(errorMessage,rules="isinstance(tg_exceptions,ValueError)")
@identity.require(identity.not_anonymous())
def invite(self, target=None):
import turbomail
@ -566,7 +109,7 @@ place for you whether you're an artist, a web site builder, a writer, or \
a people person. You'll grow and learn as you work on a team with other \
very smart and talented people. \n\
\n\
Fedora and FOSS are changing the world -- come be a part of it!") % {'name' : user.givenName, 'email' : user.mail}
Fedora and FOSS are changing the world -- come be a part of it!") % {'name': user.givenName, 'email': user.mail}
turbomail.enqueue(message)
turbogears.flash(_('Message sent to: %s') % target)
return dict(target=target, user=user)
@ -574,4 +117,3 @@ Fedora and FOSS are changing the world -- come be a part of it!") % {'name' : us
def relativeUser(realUser, sudoUser):
''' Takes user and sees if they are allow to sudo for remote group'''
p = Person.byUserName('realUser')

280
fas/fas/group.py Normal file
View file

@ -0,0 +1,280 @@
import turbogears
from turbogears import controllers, expose, paginate, identity, redirect, widgets, validate, validators, error_handler
import ldap
from fas.fasLDAP import UserAccount
from fas.fasLDAP import Person
from fas.fasLDAP import Groups
from fas.fasLDAP import UserGroup
from fas.auth import isAdmin, canAdminGroup, canSponsorGroup, canEditUser
from operator import itemgetter
from fas.user import knownUser
class knownGroup(validators.FancyValidator):
def _to_python(self, value, state):
return value.strip()
def validate_python(self, value, state):
g = Groups.groups(groupName)
if g:
raise validators.Invalid(_("The group '%s' already exists") % value, value, state)
class unknownGroup(validators.FancyValidator):
def _to_python(self, value, state):
return value.strip()
def validate_python(self, value, state):
g = Groups.groups(groupName)
if not g:
raise validators.Invalid(_("The group '%s' does not exist") % value, value, state)
class createGroup(widgets.WidgetsList):
groupName = widgets.TextField(label=_('Group Name'), validator=validators.All(knownGroup(not_empty=True, max=10), validators.String(max=32, min=3)))
fedoraGroupDesc = widgets.TextField(label=_('Description'), validator=validators.NotEmpty)
fedoraGroupOwner = widgets.TextField(label=_('Group Owner'), validator=validators.All(knownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
fedoraGroupNeedsSponsor = widgets.CheckBox(label=_('Needs Sponsor'))
fedoraGroupUserCanRemove = widgets.CheckBox(label=_('Self Removal'))
fedoraGroupJoinMsg = widgets.TextField(label=_('Group Join Message'))
createGroupForm = widgets.ListForm(fields=createGroup(), submit_text=_('Create'))
class editGroup(widgets.WidgetsList):
groupName = widgets.HiddenField(validator=validators.All(unknownGroup(not_empty=True, max=10), validators.String(max=32, min=3)))
fedoraGroupDesc = widgets.TextField(label=_('Description'), validator=validators.NotEmpty)
fedoraGroupOwner = widgets.TextField(label=_('Group Owner'), validator=validators.All(knownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
fedoraGroupNeedsSponsor = widgets.CheckBox(label=_('Needs Sponsor'))
fedoraGroupUserCanRemove = widgets.CheckBox(label=_('Self Removal'))
fedoraGroupJoinMsg = widgets.TextField(label=_('Group Join Message'))
editGroupForm = widgets.ListForm(fields=editGroup(), submit_text=_('Update'))
class findUser(widgets.WidgetsList):
userName = widgets.AutoCompleteField(label=_('Username'), search_controller='search', search_param='userName', result_name='people')
action = widgets.HiddenField(default='apply', validator=validators.String(not_empty=True))
groupName = widgets.HiddenField(validator=validators.String(not_empty=True))
findUserForm = widgets.ListForm(fields=findUser(), submit_text=_('Invite'))
class Group(controllers.Controller):
def __init__(self):
'''Create a Group Controller.'''
def index(self):
'''Perhaps show a nice explanatory message about groups here?'''
return dict()
@expose(template="fas.templates.group.view")
@identity.require(turbogears.identity.not_anonymous())
def view(self, groupName):
'''View group'''
# FIXME: Cleaner checks
try:
groups = Groups.byGroupName(groupName, includeUnapproved=True)
except KeyError:
raise ValueError, _('Group: %s - Does not exist!') % groupName
try:
group = Groups.groups(groupName)[groupName]
except TypeError:
raise ValueError, _('Group: %s - Does not exist!') % groupName
userName = turbogears.identity.current.user_name
try:
myStatus = groups[userName].fedoraRoleStatus
except KeyError:
# Not in group
myStatus = 'Not a Member' # This has say 'Not a Member'
except TypeError:
groups = {}
try:
me = groups[userName]
except:
me = UserGroup()
#searchUserForm.groupName.display('group')
#findUser.groupName.display(value='fff')
value = {'groupName': group.cn}
groups = sorted(groups.items(), key=itemgetter(0))
return dict(userName=userName, groups=groups, group=group, me=me, value=value)
@expose(template="fas.templates.group.new")
@identity.require(turbogears.identity.not_anonymous())
def new(self, groupName):
'''Create a group'''
return dict()
#@validate(form=createGroupForm)
@expose(template="fas.templates.group.new")
@identity.require(turbogears.identity.not_anonymous())
def create(self, groupName, fedoraGroupDesc, fedoraGroupOwner, fedoraGroupNeedsSponsor=True, fedoraGroupUserCanRemove=True, fedoraGroupJoinMsg=""):
userName = turbogears.identity.current.user_name
if not isAdmin(userName):
turbogears.flash(_('Only FAS adminstrators can create groups.'))
# TODO: Create a general access denied/error page.
turbogears.redirect('/')
try:
Groups.newGroup(groupName, fedoraGroupDesc, fedoraGroupOwner, fedoraGroupNeedsSponsor, fedoraGroupUserCanRemove, fedoraGroupJoinMsg)
turbogears.flash(_("The group: '%s' has been created.") % groupName)
turbogears.redirect('/group/view/%s', groupName)
except:
turbogears.flash(_("The group: '%s' could not be created.") % groupName)
return dict()
@expose(template="fas.templates.group.edit")
@identity.require(turbogears.identity.not_anonymous())
def edit(self, groupName):
'''Edit a group'''
#TODO: Handle the "no such group" case (or maybe create
#a generic function to check user/group existence.
userName = turbogears.identity.current.user_name
if not canAdminGroup(userName, groupName):
turbogears.flash(_('You cannot edit %s') % groupName)
turbogears.redirect('/group/view/%s' % groupName)
group = Groups.groups(groupName)[groupName]
value = {'groupName': groupName,
'fedoraGroupOwner': group.fedoraGroupOwner,
'fedoraGroupType': group.fedoraGroupType,
'fedoraGroupNeedsSponsor': (group.fedoraGroupNeedsSponsor.upper() == 'TRUE'),
'fedoraGroupUserCanRemove': (group.fedoraGroupUserCanRemove.upper() == 'TRUE'),
'fedoraGroupJoinMsg': group.fedoraGroupJoinMsg,
'fedoraGroupDesc': group.fedoraGroupDesc, }
#'fedoraGroupRequires': group.fedoraGroupRequires, }
return dict(value=value)
#@validate(form=editGroupForm)
@expose(template="fas.templates.group.edit")
@identity.require(turbogears.identity.not_anonymous())
def save(self, stuff):
#TODO
return dict()
@expose(template="fas.templates.group.list")
@identity.require(turbogears.identity.not_anonymous())
def list(self, search='*'):
groups = Groups.groups(search)
userName = turbogears.identity.current.user_name
myGroups = Groups.byUserName(userName)
try:
groups.keys()
except:
turbogears.flash(_("No Groups found matching '%s'") % search)
groups = {}
groups = sorted(groups.items(), key=itemgetter(0))
return dict(groups=groups, search=search, myGroups=myGroups)
# TODO: Validate
@expose(template='fas.templates.group.view')
@identity.require(turbogears.identity.not_anonymous())
def apply(self, groupName, userName):
try:
Groups.apply(groupName, userName)
except ldap.ALREADY_EXISTS:
turbogears.flash(_('%(user)s is already in %(group)s!') % {'user': userName, 'group': groupName})
turbogears.redirect('/group/view/%s' % groupName)
else:
turbogears.flash(_('%(user)s has applied to %(group)s!') % {'user': userName, 'group': groupName})
turbogears.redirect('/group/view/%s' % group.cn)
# TODO: Validate (user doesn't exist case)
@expose(template='fas.templates.group.view')
@identity.require(turbogears.identity.not_anonymous())
def sponsor(self, groupName, userName):
'''Sponsor user'''
sponsor = turbogears.identity.current.user_name
if not canSponsorGroup(sponsor, groupName):
turbogears.flash(_("You are not a sponsor for '%s'") % groupName)
turbogears.redirect('/group/view/%s' % groupName)
try:
group = Groups.groups(groupName)[groupName]
except KeyError:
turbogears.flash(_('Group Error: %s does not exist.') % groupName)
# The following line is kind of pointless- any suggestions?
turbogears.redirect('/group/view/%s' % groupName)
p = Person.byUserName(userName)
g = Groups.byGroupName(groupName, includeUnapproved=True)
# TODO: Check if the person actually applied to the group.
p.sponsor(groupName, sponsor)
turbogears.flash(_('%s has been sponsored!') % p.cn)
turbogears.redirect('/group/view/%s' % groupName)
# TODO: Validate (user doesn't exist case)
@expose(template='fas.templates.group.view')
@identity.require(turbogears.identity.not_anonymous())
def remove(self, groupName, userName):
'''Remove user from group'''
# TODO: Add confirmation?
sponsor = turbogears.identity.current.user_name
if not canSponsorGroup(sponsor, groupName) \
and sponsor != userName: # Users can remove themselves
turbogears.flash(_("You are not a sponsor for '%s'") % groupName)
turbogears.redirect('/group/view/%s' % groupName)
if canAdminGroup(userName, groupName) \
and (not canAdminGroup(sponsor, groupName)):
turbogears.flash(_('Sponsors cannot remove administrators.') % userName)
turbogears.redirect('/group/view/%s' % groupName)
try:
Groups.remove(groupName, userName)
except TypeError:
turbogears.flash(_('%(name)s could not be removed from %(group)s!') % {'name': userName, 'group': groupName})
turbogears.redirect('/group/view/%s' % groupName)
else:
turbogears.flash(_('%(name)s has been removed from %(group)s!') % {'name': userName, 'group': groupName})
turbogears.redirect('/group/view/%s' % groupName)
return dict()
# TODO: Validate (user doesn't exist case)
@expose(template='fas.templates.group.view')
@identity.require(turbogears.identity.not_anonymous())
def upgrade(self, groupName, userName):
'''Upgrade user in group'''
sponsor = turbogears.identity.current.user_name
if not canSponsorGroup(sponsor, groupName):
turbogears.flash(_("You are not a sponsor for '%s'") % groupName)
turbogears.redirect('/group/view/%s' % groupName)
# This is already checked in fasLDAP.py
#if canAdminGroup(userName, groupName):
# turbogears.flash(_('Group administators cannot be upgraded any further.'))
# turbogears.redirect('/group/view/%s' % groupName)
elif canSponsorGroup(userName, groupName) \
and (not canAdminGroup(sponsor, groupName)):
turbogears.flash(_('Sponsors cannot upgrade other sponsors.') % userName)
turbogears.redirect('/group/view/%s' % groupName)
p = Person.byUserName(userName)
try:
p.upgrade(groupName)
except:
turbogears.flash(_('%(name)s could not be upgraded!') % userName)
turbogears.redirect('/group/view/%s' % groupName)
turbogears.flash(_('%s has been upgraded!') % userName)
turbogears.redirect('/group/view/%s' % groupName)
# TODO: Validate (user doesn't exist case)
@expose(template='fas.templates.group.view')
@identity.require(turbogears.identity.not_anonymous())
def downgrade(self, groupName, userName):
'''Upgrade user in group'''
sponsor = turbogears.identity.current.user_name
if not canSponsorGroup(sponsor, groupName):
turbogears.flash(_("You are not a sponsor for '%s'") % groupName)
turbogears.redirect('/group/view/%s' % groupName)
if canAdminGroup(userName, groupName) \
and (not canAdminGroup(sponsor, groupName)):
turbogears.flash(_('Sponsors cannot downgrade group administrators.') % userName)
turbogears.redirect('/group/view/%s' % groupName)
p = Person.byUserName(userName)
try:
p.upgrade(groupName)
except:
turbogears.flash(_('%(name)s could not be downgraded!') % userName)
turbogears.redirect('/group/view/%s' % groupName)
turbogears.flash(_('%s has been downgraded!') % p.cn)
turbogears.redirect('/group/view/%s' % groupName)
# TODO: Validate (group doesn't exist case)
@expose(template="genshi-text:fas.templates.group.dump", content_type='text/plain; charset=utf-8')
@identity.require(turbogears.identity.not_anonymous())
def dump(self, groupName=None):
groups = Groups.byGroupName(groupName)
groups = sorted(groups.items(), key=itemgetter(0))
return dict(groups=groups, Person=Person)

View file

@ -407,30 +407,29 @@ pre
font-size: 3ex;
}
form ul
form
{
list-style: none;
margin: 1ex 0!important;
}
form ul li
form .field
{
margin: 0 0 1ex;
text-align: left;
overflow: hidden;
}
form ul label
form .field label
{
float: left;
clear: left;
width: 16ex;
text-align: right;
margin: 0;
padding: 0 2ex 0 0;
}
form ul input, form ul textarea
form .field input, form .field textarea
{
margin: 0;
}

View file

@ -1,3 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
</html>

View file

@ -1,7 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#">
<body>
<div py:for="user in groups">${user},${Person.byUserName(user).mail},${Person.byUserName(user).givenName},${groups[user].fedoraRoleType},0
</div>
</body>
</html>

View file

@ -1,10 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<head>
<title>Edit Account</title>
</head>
<body>
<h2>Edit Account</h2>
${form(action='editAccountSubmit', method='post', value=value)}
</body>
</html>

View file

@ -1,10 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<head>
<title>Edit Group</title>
</head>
<body>
<h2>Edit Group</h2>
${form(action='editGroupSubmit', method='post', value=value)}
</body>
</html>

View file

@ -1,9 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<head>
<title>Crap!</title>
</head>
<body>
${exception}
</body>
</html>

View file

Binary file not shown.

View file

@ -0,0 +1,3 @@
#for user in groups
${user[0].decode('utf-8')},${Person.byUserName(user[0]).mail.decode('utf-8')},${Person.byUserName(user[0]).givenName.decode('utf-8')},${user[1].fedoraRoleType.decode('utf-8')}
#end

View file

@ -0,0 +1,39 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../master.html" />
<head>
<title>Edit Group</title>
</head>
<body>
<h2>Edit Group</h2>
<form action="${tg.url('/group/save/%s' % value['groupName'])}" method="post">
<div class="field">
<label for="fedoraGroupDesc">Description:</label>
<input type="text" id="fedoraGroupDesc" name="fedoraGroupDesc" value="${value['fedoraGroupDesc']}" />
</div>
<div class="field">
<label for="fedoraGroupOwner">Group Owner:</label>
<input type="text" id="fedoraGroupOwner" name="fedoraGroupOwner" value="${value['fedoraGroupOwner']}" />
</div>
<div class="field">
<label for="fedoraGroupNeedsSponsor">Needs Sponsor:</label>
<input py:if="value['fedoraGroupNeedsSponsor']" type="checkbox" id="fedoraGroupNeedsSponsor" name="fedoraGroupNeedsSponsor" checked="checked" />
<input py:if="not value['fedoraGroupNeedsSponsor']" type="checkbox" id="fedoraGroupNeedsSponsor" name="fedoraGroupNeedsSponsor" />
</div>
<div class="field">
<label for="fedoraGroupUserCanRemove">Self Removal:</label>
<input py:if="value['fedoraGroupUserCanRemove']" type="checkbox" id="fedoraGroupUserCanRemove" name="fedoraGroupUserCanRemove" checked="checked" />
<input py:if="not value['fedoraGroupUserCanRemove']" type="checkbox" id="fedoraGroupUserCanRemove" name="fedoraGroupUserCanRemove" />
</div>
<div class="field">
<label for="fedoraGroupJoinMsg">Group Join Message:</label>
<input type="text" id="fedoraGroupJoinMsg" name="fedoraGroupJoinMsg" value="${value['fedoraGroupJoinMsg']}" />
</div>
<div class="field">
<input type="submit" value="Save!" />
</div>
</form>
</body>
</html>

View file

@ -0,0 +1,44 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../master.html" />
<head>
<title>Groups List</title>
</head>
<body>
<h2>List (${search})</h2>
<h3>Search Groups</h3>
<form method="get" action="${tg.url('/group/list')}">
<p>"*" is a wildcard (Ex: "cvs*")</p>
<div>
<input type="text" value="${search}" name="search" size="15 "/>
<input type="submit" value="Search" />
</div>
</form>
<h3>Results</h3>
<ul class="letters">
<li py:for="letter in 'abcdefghijklmnopqrstuvwxyz'.upper()"><a href="${tg.url('/group/list/%s*' % letter)}">${letter}</a></li>
<li><a href="${tg.url('/group/list/*')}">All</a></li>
</ul>
<table py:if="groups">
<thead>
<tr><th>Group</th><th>Description</th><th>Status</th></tr>
</thead>
<tbody>
<tr py:for="group in groups">
<td><a href="${tg.url('/group/view/%s' % group[1].cn)}">${group[1].cn}</a></td>
<td>${group[1].fedoraGroupDesc}</td>
<td>
<a py:if="group[1].cn in myGroups" href="${tg.url('/group/view/%s' % group[1].cn)}">
<span class="approved" py:if="myGroups[group[1].cn].fedoraRoleStatus.lower() == 'approved'">Approved</span>
<span class="unapproved" py:if="myGroups[group[1].cn].fedoraRoleStatus.lower() == 'unapproved'">Unapproved</span>
</a>
<a py:if="group[1].cn not in myGroups" href="${tg.url('/group/view/%s' % group[1].cn)}"><span>Not a Member</span></a>
</td>
</tr>
</tbody>
</table>
</body>
</html>

View file

@ -0,0 +1,41 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../master.html" />
<head>
<title>Create a new FAS Group</title>
</head>
<body>
<h2>Create a new FAS Group</h2>
<form action="${tg.url('/group/create')}" method="post">
<div class="field">
<label for="groupName">Group Name:</label>
<input type="text" id="groupName" name="groupName" />
</div>
<div class="field">
<label for="fedoraGroupDesc">Description:</label>
<input type="text" id="fedoraGroupDesc" name="fedoraGroupDesc" />
</div>
<div class="field">
<label for="fedoraGroupOwner">Group Owner:</label>
<input type="text" id="fedoraGroupOwner" name="fedoraGroupOwner" />
</div>
<div class="field">
<label for="fedoraGroupNeedsSponsor">Needs Sponsor:</label>
<input type="checkbox" id="fedoraGroupNeedsSponsor" name="fedoraGroupNeedsSponsor" />
</div>
<div class="field">
<label for="fedoraGroupUserCanRemove">Self Removal:</label>
<input type="checkbox" id="fedoraGroupUserCanRemove" name="fedoraGroupUserCanRemove" />
</div>
<div class="field">
<label for="fedoraGroupJoinMsg">Group Join Message:</label>
<input type="text" id="fedoraGroupJoinMsg" name="fedoraGroupJoinMsg" value="${value['fedoraGroupJoinMsg']}" />
</div>
<div class="field">
<input type="submit" value="Create!" />
</div>
</form>
</body>
</html>

View file

@ -0,0 +1,87 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../master.html" />
<head>
<title>Edit Group</title>
</head>
<body>
<h2>${group.fedoraGroupDesc} (${group.cn})</h2>
<h3>
My Status:
<span py:if="me.fedoraRoleStatus.lower() == 'approved'" class="approved">Approved</span>
<span py:if="me.fedoraRoleStatus.lower() == 'unapproved'" class="unapproved">Unapproved</span>
<span py:if="'Not a Member' in me.fedoraRoleStatus">Not a Member</span>
</h3>
<form py:if="'Not a Member' in me.fedoraRoleStatus" action="${tg.url('/group/apply/%s/%s' % (group.cn, userName))}">
<div>
<!--<input type="text" name="requestField" value="Please let me join..." />-->
<input type="submit" value="Join!" />
</div>
</form>
<a py:if="'Not a Member' not in me.fedoraRoleStatus" href="${tg.url('/group/remove/%s/%s' % (group.cn, userName))}">Remove me</a>
<h3>Group Details <a href="${tg.url('/group/edit/%s' % group.cn)}">(edit)</a></h3>
<div class="userbox">
<dl>
<dt>Name:</dt><dd>${group.cn}</dd>
<dt>Description:</dt><dd>${group.fedoraGroupDesc}</dd>
<dt>Owner:</dt><dd>${group.fedoraGroupOwner}</dd>
<dt>Type:</dt><dd>${group.fedoraGroupType}</dd>
<dt>Needs Sponsor:</dt><dd>
<span py:if="group.fedoraGroupNeedsSponsor == 'TRUE'" py:strip="">Yes</span>
<span py:if="group.fedoraGroupNeedsSponsor == 'FALSE'" py:strip="">No</span>
</dd>
<dt>Self Removal</dt><dd>
<span py:if="group.fedoraGroupUserCanRemove == 'TRUE'" py:strip="">Yes</span>
<span py:if="group.fedoraGroupUserCanRemove == 'FALSE'" py:strip="">No</span>
</dd>
<dt>Join Message:</dt><dd>${group.fedoraGroupJoinMsg}</dd>
</dl>
</div>
<!--
TODO: Implement this :)
<h3 py:if='me.fedoraRoleStatus == "approved"'>Invite</h3>
<span py:if='me.fedoraRoleStatus == "approved"'>${form(action='modifyGroup', value=value, method='get')}</span>
-->
<h3>Members</h3>
<table>
<thead>
<tr>
<th>Username</th>
<th>Sponsor</th>
<th>Date Added</th>
<th>Date Approved</th>
<th>Approval</th>
<th>Role Type</th>
<th py:if='me.fedoraRoleType == "administrator" or me.fedoraRoleType == "sponsor"'>Action</th>
</tr>
</thead>
<tr py:for="user in groups">
<td><a href="${tg.url('/user/view/%s' % user[0])}">${user[0]}</a></td>
<td py:if='not(user[1].fedoraRoleSponsor == "None")'><a href="%{tg.url('/user/view/%s' % user[1].fedoraRoleSponsor)}">${user[1].fedoraRoleSponsor}</a></td>
<td py:if='user[1].fedoraRoleSponsor == "None"'>${user[1].fedoraRoleSponsor}</td>
<td>${user[1].fedoraRoleCreationDate}</td>
<td>${user[1].fedoraRoleApprovalDate}</td>
<td>${user[1].fedoraRoleStatus}</td>
<td>${user[1].fedoraRoleType}</td>
<!--<td>${user[1].fedoraRoleDomain}</td>-->
<!-- This section includes all action items -->
<td py:if='me.fedoraRoleType == "administrator"'>
<a py:if="group.fedoraGroupNeedsSponsor.upper() == 'TRUE'" href="${tg.url('/group/sponsor/%s/%s' % (user[1].cn, user[0]))}">Sponsor</a>
<a py:if="not group.fedoraGroupNeedsSponsor.upper() == 'TRUE' and user[1].fedoraRoleStatus.lower() != 'approved'" href="${tg.url('/group/sponsor/%s/%s' % (user[1].cn, user[0]))}">Approve</a>
<a href="${tg.url('/group/remove/%s/%s' % (user[1].cn, user[0]))}">Delete</a>
<a href="${tg.url('/group/upgrade/%s/%s' % (user[1].cn, user[0]))}">Upgrade</a>
<a href="${tg.url('/group/downgrade/%s/%s' % (user[1].cn, user[0]))}">Downgrade</a> Suspend
</td>
<td py:if='me.fedoraRoleType == "sponsor" and not user[1].fedoraRoleType == "administrator"'>
<a href="${tg.url('/group/sponsor/%s/%s' % (user[1].cn, user[0]))}" py:if="group.fedoraGroupNeedsSponsor.upper() == 'TRUE'">Sponsor</a>
<a href="${tg.url('/group/sponsor/%s/%s' % (user[1].cn, user[0]))}" py:if="not group.fedoraGroupNeedsSponsor.upper() == 'TRUE'">Approve</a>
<a href="${tg.url('/group/remove/%s/%s' % (user[1].cn, user[0]))}">Delete</a>
<a py:if='user[1].fedoraRoleType' href="${tg.url('/group/upgrade/%s/%s' % (user[1].cn, user[0]))}">Upgrade</a>
<a href="${tg.url('/group/downgrade/%s/%s' % (user[1].cn, user[0]))}">Downgrade</a> Suspend
</td>
</tr>
</table>
</body>
</html>

View file

@ -1,46 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
py:extends="'master.kid'">
<head>
<title>Groups List</title>
</head>
<body>
<h2>List (${search})</h2>
<h3>Search Groups</h3>
<form method="GET" action="${tg.url('/listGroup')}">
<p>"*" is a wildcard (Ex: "cvs*")</p>
<div>
<input type="text" value="${search}" name="search" size="15 "/>
<input type="submit" value="Search" />
</div>
</form>
<h3>Results</h3>
<ul class="letters">
<li py:for="letter in 'abcdefghijklmnopqrstuvwxyz'.upper()"><a href="${tg.url('/listGroup/%s*' % letter)}">${letter}</a></li>
<li><a href="${tg.url('/listGroup/*')}">All</a></li>
</ul>
<table py:if="groups">
<thead>
<tr><th>Group</th><th>Description</th><th>Status</th></tr>
</thead>
<tbody>
<?python
keys = groups.keys()
keys.sort()
?>
<tr py:for="group in map(groups.get, keys)">
<td><a href="${tg.url('/viewGroup/%s' % group.cn)}">${group.cn}</a></td>
<td>${group.fedoraGroupDesc}</td>
<td>
<a py:if="group.cn in myGroups" href="${tg.url('/viewGroup/%s' % group.cn)}">
<span class="approved" py:if="myGroups[group.cn].fedoraRoleStatus.lower() == 'approved'">Approved</span>
<span class="unapproved" py:if="myGroups[group.cn].fedoraRoleStatus.lower() == 'unapproved'">Unapproved</span>
</a>
<a py:if="group.cn not in myGroups" href="${tg.url('/viewGroup/%s' % group.cn)}"><span>Not a Member</span></a>
</td>
</tr>
</tbody>
</table>
</body>
</html>

View file

@ -1,5 +1,8 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<title>Fedora Accounts System</title>
</head>
@ -18,6 +21,5 @@
<td>${builds.builds[build]['pubDate']}</td>
</tr>
</table>
</body>
</html>

View file

@ -1,11 +1,14 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<title>Invite a new community member!</title>
</head>
<body>
<h2>Invite a new community member!</h2>
<form method="POST">
<form method="post">
<div>
To email: <input type="text" value="" name="target" /><br />
From: ${user.mail}<br />

View file

@ -0,0 +1,33 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<title>Login to the Fedora Accounts System</title>
</head>
<style type="text/css">
#content ul
{
list-style: square;
margin: 1ex 3ex;
}
</style>
<body>
<h2>Login</h2>
<p>${message}</p>
<form action="${previous_url}" method="post">
<div class="field"><label for="user_name">User Name:</label> <input type="text" id="user_name" name="user_name" /></div>
<div class="field"><label for="password">Password:</label> <input type="password" id="password" name="password" /></div>
<div class="field">
<input type="submit" name="login" value="Login" />
<input py:if="forward_url" type="hidden" name="forward_url" value="${tg.url(forward_url)}" />
<input py:for="name,value in original_parameters.items()" type="hidden" name="${name}" value="${value}" />
</div>
</form>
<ul>
<li><a href="${tg.url('/user/resetpass')}">Forgot Password?</a></li>
<li><a href="${tg.url('/user/new')}">Sign Up</a></li>
</ul>
</body>
</html>

View file

@ -1,33 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<head>
<title>Login to the Fedora Accounts System</title>
</head>
<style type="text/css">
#content ul
{
list-style: square;
margin: 1ex 3ex;
}
</style>
<body>
<h2>Login</h2>
<p>${message}</p>
<form action="${previous_url}" method="POST">
<ul>
<li><label for="user_name">User Name:</label> <input type="text" id="user_name" name="user_name" /></li>
<li><label for="password">Password:</label> <input type="password" id="password" name="password" /></li>
<li>
<input type="submit" name="login" value="Login" />
<input py:if="forward_url" type="hidden" name="forward_url" value="${tg.url(forward_url)}" />
<input py:for="name,value in original_parameters.items()" type="hidden" name="${name}" value="${value}" />
</li>
</ul>
</form>
<ul>
<li><a href="${tg.url('/resetPassword')}">Forgot Password?</a></li>
<li><a href="${tg.url('/signUp')}">Sign Up</a></li>
</ul>
</body>
</html>

View file

@ -1,12 +1,13 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<?python import sitetemplate ?>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="sitetemplate">
<head py:match="item.tag=='{http://www.w3.org/1999/xhtml}head'" py:attrs="item.items()">
<title py:replace="''">Title</title>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:py="http://genshi.edgewall.org/"
py:strip="">
<head>
<title>${title}</title>
<link href="${tg.url('/static/css/style.css')}" rel="stylesheet" type="text/css" />
<meta py:replace="item[:]"/>
</head>
<body py:match="item.tag=='{http://www.w3.org/1999/xhtml}body'" py:attrs="item.items()">
<body py:match="body" py:attrs="select('@*')">
<div id="wrapper">
<div id="head">
<h1><a href="http://fedoraproject.org/">Fedora</a></h1>
@ -36,7 +37,7 @@
</div>
<div id="control">
<ul>
<li py:if="not tg.identity.anonymous"><a href="${tg.url('/viewAccount')}">My Account</a></li>
<li py:if="not tg.identity.anonymous"><a href="${tg.url('/user/view/%s' % tg.identity.user.user_name)}">My Account</a></li>
<li py:if="not tg.identity.anonymous"><a href="${tg.url('/logout')}">Log Out</a></li>
<li py:if="tg.identity.anonymous"><a href="${tg.url('/login')}">Log In</a></li>
</ul>
@ -45,17 +46,17 @@
<div id="main">
<div id="sidebar">
<ul>
<li class="first"><a href="${tg.url('/listGroup')}">Group List</a></li>
<li py:if="'accounts' in tg.identity.groups"><a href="${tg.url('/listUser')}">User List</a></li>
<li class="first"><a href="${tg.url('/group/list')}">Group List</a></li>
<li py:if="'accounts' in tg.identity.groups"><a href="${tg.url('/user/list')}">User List</a></li>
<li><a href="http://fedoraproject.org/wiki/FWN/LatestIssue">News</a></li>
<li><a href="${tg.url('/listGroup', search='A*')}">Apply For a new Group</a></li>
<li><a href="${tg.url('/group/list/A*')}">Apply For a new Group</a></li>
</ul>
</div>
<div id='content'>
<div py:if="tg_flash" class="flash">
${tg_flash}
</div>
<div py:replace="[item.text]+item[:]" />
<div py:replace="select('*|text()')" />
</div> <!-- End main -->
<div id="footer">
<ul id="footlinks">

View file

@ -1,30 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<head>
<title>Reset Password</title>
</head>
<body>
<h2>Reset Password</h2>
<div py:if="tg.identity.anonymous" py:strip="">
<form method="post">
<ul>
<li><label for="userName">Username:</label> <input type="text" id="userName" name="userName" /></li>
<li><label for="mail">Primary Email:</label> <input type="password" id="mail" name="mail" /></li>
<li><input type="submit" value="Reset Password" /></li>
</ul>
</form>
</div>
<div py:if="not tg.identity.anonymous" py:strip="">
<p>
New password for ${tg.identity.user.user_name}
</p>
<form method="post">
<ul>
<li><label for="password">New Password:</label> <input type="password" name="password" /></li>
<li><label for="password">Verify Password:</label> <input type="password" name="passwordCheck" /></li>
<li><input type="submit" /></li>
</ul>
</form>
</div>
</body>
</html>

View file

@ -1,10 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<head>
<title>Sign up for a Fedora account</title>
</head>
<body>
<h2>Sign up for a Fedora account</h2>
${form(action='newAccountSubmit', method='post')}
</body>
</html>

View file

Binary file not shown.

View file

@ -0,0 +1,20 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../master.html" />
<head>
<title>Change Password</title>
</head>
<body>
<h2>Change Password</h2>
<form action="${tg.url('/user/setpass')}" method="post">
<ul>
<div class="field"><label for="currentPassword">Current Password:</label> <input type="password" id="currentPassword" name="currentPassword" /></div>
<div class="field"><label for="password">New Password:</label> <input type="password" id="password" name="password" /></div>
<div class="field"><label for="passwordCheck">Confirm Password:</label> <input type="password" id="passwordCheck" name="passwordCheck" /></div>
<div class="field"><input type="submit" value="Change Password" /></div>
</ul>
</form>
</body>
</html>

View file

@ -0,0 +1,51 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../master.html" />
<head>
<title>Edit Account</title>
</head>
<body>
<h2>Edit Account</h2>
<form action="${tg.url('/user/save/%s' % value['userName'])}" method="post">
<div class="field">
<label for="givenName">Full Name:</label>
<input type="text" id="givenName" name="givenName" value="${value['givenName']}" />
</div>
<div class="field">
<label for="mail">Email:</label>
<input type="text" id="mail" name="mail" value="${value['mail']}" />
</div>
<div class="field">
<label for="fedoraPersonBugzillaMail">Bugzilla Email:</label>
<input type="text" id="fedoraPersonBugzillaMail" name="fedoraPersonBugzillaMail" value="${value['fedoraPersonBugzillaMail']}" />
</div>
<div class="field">
<label for="fedoraPersonIrcNick">IRC Nick:</label>
<input type="text" id="fedoraPersonIrcNick" name="fedoraPersonIrcNick" value="${value['fedoraPersonIrcNick']}" />
</div>
<div class="field">
<label for="fedoraPersonKeyId">PGP Key:</label>
<input type="text" id="fedoraPersonKeyId" name="fedoraPersonKeyId" value="${value['fedoraPersonKeyId']}" />
</div>
<div class="field">
<label for="telephoneNumber">Telephone Number:</label>
<input type="text" id="telephoneNumber" name="telephoneNumber" value="${value['telephoneNumber']}" />
</div>
<div class="field">
<label for="postalAddress">Postal Address:</label>
<input type="text" id="postalAddress" name="postalAddress" value="${value['postalAddress']}" />
</div>
<div class="field">
<label for="description ">Description:</label>
<textarea id="description" name="description">
${value['description']}
</textarea>
</div>
<div class="field">
<input type="submit" value="Save!" />
</div>
</form>
</body>
</html>

View file

@ -1,11 +1,14 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../master.html" />
<head>
<title>Users List</title>
</head>
<body>
<h2>List (${search})</h2>
<form method="GET" action="${tg.url('/listUser')}">
<form method="get" action="${tg.url('/user/list')}">
<p>"*" is a wildcard (Ex: "cvs*")</p>
<div>
<input type="text" value="${search}" name="search" size="15 "/>
@ -14,8 +17,8 @@
</form>
<h3>Results</h3>
<ul class="letters">
<li py:for="letter in 'abcdefghijklmnopqrstuvwxyz'.upper()"><a href="${tg.url('/listUser/%s*' % letter)}">${letter}</a></li>
<li><a href="${tg.url('/listUser/*')}">All</a></li>
<li py:for="letter in 'abcdefghijklmnopqrstuvwxyz'.upper()"><a href="${tg.url('/user/list/%s*' % letter)}">${letter}</a></li>
<li><a href="${tg.url('/user/list/*')}">All</a></li>
</ul>
<table>
<thead>
@ -25,11 +28,8 @@
</tr>
</thead>
<tbody>
<?python
users.sort()
?>
<tr py:for="user in users">
<td><a href="${tg.url('/viewAccount/%s' % user)}">${user}</a></td>
<td><a href="${tg.url('/user/view/%s' % user)}">${user}</a></td>
<td>
<span py:if="claDone[user]" class="approved"> Done</span>
<span py:if="not claDone[user]" class="unapproved"> Done</span>

View file

@ -0,0 +1,37 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../master.html" />
<head>
<title>Sign up for a Fedora account</title>
</head>
<body>
<h2>Sign up for a Fedora account</h2>
<form action="${tg.url('/user/create')}" method="post">
<div class="field">
<label for="cn">Username:</label>
<input type="text" id="cn" name="cn" />
</div>
<div class="field">
<label for="givenName">Full Name:</label>
<input type="text" id="givenName" name="givenName" />
</div>
<div class="field">
<label for="mail">Email</label>
<input type="text" id="mail" name="mail" />
</div>
<div class="field">
<label for="telephoneNumber">Telephone Number</label>
<input type="text" id="telephoneNumber" name="telephoneNumber" />
</div>
<div class="field">
<label for="postalAddress">Postal Address</label>
<input type="text" id="postalAddress" name="postalAddress" />
</div>
<div class="field">
<input type="submit" value="Sign up!" />
</div>
</form>
</body>
</html>

View file

@ -0,0 +1,19 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../master.html" />
<head>
<title>Reset Password</title>
</head>
<body>
<h2>Reset Password</h2>
<form action="${tg.url('/user/sendpass')}" method="post">
<ul>
<div class="field"><label for="userName">Username:</label> <input type="text" id="userName" name="userName" /></div>
<div class="field"><label for="mail">Primary Email:</label> <input type="password" id="mail" name="mail" /></div>
<div class="field"><input type="submit" value="Reset Password" /></div>
</ul>
</form>
</body>
</html>

View file

@ -1,12 +1,15 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="../master.html" />
<head>
<title>View Account</title>
</head>
<body>
<h2 class="account" py:if="personal">Your Fedora Account</h2>
<h2 class="account" py:if="not personal">${user.givenName}'s Fedora Account</h2>
<h3>Account Details <a href="${tg.url('/editAccount', userName=user.cn)}" py:if="personal or admin">(edit)</a></h3>
<h3>Account Details <a href="${tg.url('/user/edit/%s' % user.cn)}" py:if="personal or admin">(edit)</a></h3>
<div class="userbox">
<dl>
<dt>Account Name</dt><dd>${user.cn}</dd>
@ -18,30 +21,24 @@
<dt>Telephone Number</dt><dd>${user.telephoneNumber}</dd>
<dt>Postal Address</dt><dd>${user.postalAddress}</dd>
<dt>Description</dt><dd>${user.description}</dd>
<dt>Password</dt><dd><span class="approved">Valid</span> <a href="${tg.url('/resetPassword')}" py:if="personal">(change)</a></dd>
<dt>Password</dt><dd><span class="approved">Valid</span> <a href="${tg.url('/user/changepass')}" py:if="personal">(change)</a></dd>
<dt>Account Status</dt><dd><span class="approved">Approved</span>, Active</dd>
<dt>CLA</dt><dd><span py:if="claDone" class="approved">Done</span><span py:if="not claDone" class="unapproved"> Not Done</span></dd>
</dl>
</div>
<h3 py:if="personal">Your Roles</h3>
<h3 py:if="not personal">${user.givenName}'s Roles</h3>
<?python
keys = groups.keys()
keys.sort()
keysPending = groupsPending.keys()
keysPending.sort()
?>
<ul class="roleslist">
<li py:for="group in map(groups.get, keys)"><span class="team approved">${groupdata[group.cn].fedoraGroupDesc} (${group.cn})</span></li>
<li py:for="group in map(groupsPending.get, keysPending)"><span class="team unapproved">${groupdata[group.cn].fedoraGroupDesc} (${group.cn})</span></li>
<li py:for="group in groups"><span class="team approved">${groupdata[group[0]].fedoraGroupDesc} (${group[0]})</span></li>
<li py:for="group in groupsPending"><span class="team unapproved">${groupdata[group[0]].fedoraGroupDesc} (${group[0]})</span></li>
</ul>
<ul class="actions" py:if="personal">
<li><a href="/">(Join another project)</a></li>
<li><a href="/">(Create a new project)</a></li>
</ul>
<ul id="rolespanel" py:if="personal">
<li py:for="group in map(groups.get, keys)" class="role">
<h4>${groupdata[group.cn].fedoraGroupDesc}</h4>, ${group.fedoraRoleType}
<li py:for="group in groups" class="role">
<h4>${groupdata[group[0]].fedoraGroupDesc}</h4>, ${group[1].fedoraRoleType}
<dl>
<dt>Status:</dt>
<dd>
@ -51,12 +48,13 @@
<dd>
<ul class="tools">
<li><a href="/">Invite a New Member...</a></li>
<li py:if="group.fedoraRoleType.lower() in ('administrator', 'sponsor')"><a href="${tg.url('/viewGroup', groupName=group.cn)}">View All Pending Group Membership Requests...</a></li>
<li><a href="${tg.url('/viewGroup', groupName=group.cn)}">Manage Group Membership...</a></li>
<li py:if="group.fedoraRoleType.lower() == 'administrator'"><a href="${tg.url('/editGroup', groupName=group.cn)}">Manage Group Details...</a></li>
<li py:if="group[1].fedoraRoleType.lower() in ('administrator', 'sponsor')"><a href="${tg.url('/group/view/%s' % group[0])}">View All Pending Group Membership Requests...</a></li>
<li><a href="${tg.url('/group/view/%s' % group[0])}">Manage Group Membership...</a></li>
<!-- Replace with canAdminGroup, etc. -->
<li py:if="group[1].fedoraRoleType.lower() == 'administrator'"><a href="${tg.url('/group/edit/%s' % group[0])}">Manage Group Details...</a></li>
</ul>
</dd>
<div py:if="group.fedoraRoleType.lower() in ('administrator', 'sponsor')" py:strip="">
<div py:if="group[1].fedoraRoleType.lower() in ('administrator', 'sponsor')" py:strip="">
<dt>Queue:</dt>
<dd>
<ul class="queue">

View file

@ -1,84 +0,0 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#"
py:extends="'master.kid'">
<head>
<title>Edit Group</title>
</head>
<body>
<h2>${group.fedoraGroupDesc} (${group.cn})</h2>
<h3>
My Status:
<span py:if="me.fedoraRoleStatus.lower() == 'approved'" class="approved">Approved</span>
<span py:if="me.fedoraRoleStatus.lower() == 'unapproved'" class="unapproved">Unapproved</span>
<span py:if="'Not a Member' in me.fedoraRoleStatus">Not a Member</span>
</h3>
<form py:if="'Not a Member' in me.fedoraRoleStatus" action="${tg.url('/applyForGroup')}">
<div>
<input type="hidden" name="groupName" value="${group.cn}" />
<input type="text" name="requestField" value="Please let me join.." />
<input type="submit" name="action" value="Join" />
</div>
</form>
<a py:if="'Not a Member' not in me.fedoraRoleStatus" href="${tg.url('/applyForGroup', groupName=group.cn, action='Remove')}">Remove me</a>
<h3>Group Details <a href="${tg.url('editGroup', groupName=group.cn)}">(edit)</a></h3>
<div class="userbox">
<dl>
<dt>Name:</dt><dd>${group.cn}</dd>
<dt>Description:</dt><dd>${group.fedoraGroupDesc}</dd>
<dt>Owner:</dt><dd>${group.fedoraGroupOwner}</dd>
<dt>Type:</dt><dd>${group.fedoraGroupType}</dd>
<dt>Needs Sponsor:</dt><dd>
<span py:if="group.fedoraGroupNeedsSponsor == 'TRUE'" py:strip="">Yes</span>
<span py:if="group.fedoraGroupNeedsSponsor == 'FALSE'" py:strip="">No</span>
</dd>
<dt>Self Removal</dt><dd>
<span py:if="group.fedoraGroupUserCanRemove == 'TRUE'" py:strip="">Yes</span>
<span py:if="group.fedoraGroupUserCanRemove == 'FALSE'" py:strip="">No</span>
</dd>
<dt>Join Message:</dt><dd>${group.fedoraGroupJoinMsg}</dd>
</dl>
</div>
<h3 py:if='me.fedoraRoleStatus == "approved"'>Invite</h3>
<span py:if='me.fedoraRoleStatus == "approved"'>${searchUserForm(action='modifyGroup', value=value, method='get')}</span>
<h3>Members</h3>
<table>
<thead>
<tr>
<th>Username</th>
<th>Sponsor</th>
<th>Date Added</th>
<th>Date Approved</th>
<th>Approval</th>
<th>Role Type</th>
<th py:if='me.fedoraRoleType == "administrator" or me.fedoraRoleType == "sponsor"'>Action</th>
</tr>
</thead>
<tr py:for="user in groups">
<td><a href="${tg.url('viewAccount/%s' % user)}">${user}</a></td>
<td py:if='not(groups[user].fedoraRoleSponsor == "None")'><a href="%{tg.url('viewAccount/%s' % groups[user].fedoraRoleSponsor)}">${groups[user].fedoraRoleSponsor}</a></td>
<td py:if='groups[user].fedoraRoleSponsor == "None"'>${groups[user].fedoraRoleSponsor}</td>
<td>${groups[user].fedoraRoleCreationDate}</td>
<td>${groups[user].fedoraRoleApprovalDate}</td>
<td>${groups[user].fedoraRoleStatus}</td>
<td>${groups[user].fedoraRoleType}</td>
<!--<td>${groups[user].fedoraRoleDomain}</td>-->
<!-- This section includes all action items -->
<td py:if='me.fedoraRoleType == "administrator"'>
<a py:if="group.fedoraGroupNeedsSponsor.upper() == 'TRUE'" href="${tg.url('/modifyGroup', groupName=groups[user].cn, userName=user, action='sponsor')}">Sponsor</a>
<a py:if="not group.fedoraGroupNeedsSponsor.upper() == 'TRUE' and groups[user].fedoraRoleStatus.lower() != 'approved'" href="${tg.url('/modifyGroup', groupName=groups[user].cn, userName=user, action='sponsor')}">Approve</a>
<a href="${tg.url('/modifyGroup', groupName=groups[user].cn, userName=user, action='remove')}">Delete</a>
<a href="${tg.url('/modifyGroup', groupName=groups[user].cn, userName=user, action='upgrade')}">Upgrade</a>
<a href="${tg.url('/modifyGroup', groupName=groups[user].cn, userName=user, action='downgrade')}">Downgrade</a> Suspend
</td>
<td py:if='me.fedoraRoleType == "sponsor" and not groups[user].fedoraRoleType == "administrator"'>
<a href="${tg.url('/modifyGroup', groupName=groups[user].cn, userName=user, action='sponsor')}" py:if="group.fedoraGroupNeedsSponsor.upper() == 'TRUE'">Sponsor</a>
<a href="${tg.url('/modifyGroup', groupName=groups[user].cn, userName=user, action='sponsor')}" py:if="not group.fedoraGroupNeedsSponsor.upper() == 'TRUE'">Approve</a>
<a href="${tg.url('/modifyGroup', groupName=groups[user].cn, userName=user, action='remove')}">Delete</a>
<a py:if='groups[user].fedoraRoleType' href="${tg.url('/modifyGroup', groupName=groups[user].cn, userName=user, action='upgrade')}">Upgrade</a>
<a href="${tg.url('/modifyGroup', groupName=groups[user].cn, userName=user, action='downgrade')}">Downgrade</a> Suspend
<div py:if="'not' in '%s' % tg_flash and user in '%s' % tg_flash"> -- Error!</div><!-- Clean this up -->
</td>
</tr>
</table>
</body>
</html>

View file

@ -1,5 +1,8 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:py="http://purl.org/kid/ns#" py:extends="'master.kid'">
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="master.html" />
<head>
<title>Welcome to FAS2</title>
<style type="text/css">
@ -16,7 +19,7 @@
</p>
<ul>
<li><a href="${tg.url('/login')}">Log In</a></li>
<li><a href="${tg.url('/signUp')}">New Account</a></li>
<li><a href="${tg.url('/user/new')}">New Account</a></li>
<li><a href="http://fedoraproject.org/wiki/Join">Why Join?</a></li>
</ul>
</body>

253
fas/fas/user.py Normal file
View file

@ -0,0 +1,253 @@
import turbogears
from turbogears import controllers, expose, paginate, identity, redirect, widgets, validate, validators, error_handler
import ldap
from fas.fasLDAP import UserAccount
from fas.fasLDAP import Person
from fas.fasLDAP import Groups
from fas.fasLDAP import UserGroup
from fas.auth import isAdmin, canAdminGroup, canSponsorGroup, canEditUser
from operator import itemgetter
class knownUser(validators.FancyValidator):
def _to_python(self, value, state):
return value.strip()
def validate_python(self, value, state):
p = Person.byUserName(value)
if p.cn:
raise validators.Invalid(_("'%s' already exists") % value, value, state)
class unknownUser(validators.FancyValidator):
def _to_python(self, value, state):
return value.strip()
def validate_python(self, value, state):
p = Person.byUserName(value)
if not p.cn:
raise validators.Invalid(_("'%s' does not exist") % value, value, state)
class editUser(widgets.WidgetsList):
# cn = widgets.TextField(label='Username', validator=validators.PlainText(not_empty=True, max=10))
userName = widgets.HiddenField(validator=validators.All(unknownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
givenName = widgets.TextField(label=_('Full Name'), validator=validators.String(not_empty=True, max=42))
mail = widgets.TextField(label=_('Email'), validator=validators.Email(not_empty=True, strip=True))
fedoraPersonBugzillaMail = widgets.TextField(label=_('Bugzilla Email'), validator=validators.Email(not_empty=True, strip=True))
fedoraPersonIrcNick = widgets.TextField(label=_('IRC Nick'))
fedoraPersonKeyId = widgets.TextField(label=_('PGP Key'))
telephoneNumber = widgets.TextField(label=_('Telephone Number'), validator=validators.PhoneNumber(not_empty=True))
postalAddress = widgets.TextArea(label=_('Postal Address'), validator=validators.NotEmpty)
description = widgets.TextArea(label=_('Description'))
editUserForm = widgets.ListForm(fields=editUser(), submit_text=_('Update'))
class newUser(widgets.WidgetsList):
#cn = widgets.TextField(label='Username', validator=validators.PlainText(not_empty=True, max=10))
cn = widgets.TextField(label=_('Username'), validator=validators.All(knownUser(not_empty=True, max=10), validators.String(max=32, min=3)))
givenName = widgets.TextField(label=_('Full Name'), validator=validators.String(not_empty=True, max=42))
mail = widgets.TextField(label=_('Email'), validator=validators.Email(not_empty=True, strip=True))
telephoneNumber = widgets.TextField(label=_('Telephone Number'), validator=validators.PhoneNumber(not_empty=True))
postalAddress = widgets.TextArea(label=_('Postal Address'), validator=validators.NotEmpty)
newUserForm = widgets.ListForm(fields=newUser(), submit_text=_('Sign Up'))
class User(controllers.Controller):
def __init__(self):
'''Create a User Controller.
'''
def index(self):
'''Redirect to view
'''
turbogears.redirect('view/%s' % turbogears.identity.current.user_name)
@expose(template="fas.templates.user.view")
@identity.require(turbogears.identity.not_anonymous())
def view(self, userName=None):
'''View a User.
'''
# TODO: Validate- check if user actually exists
if not userName:
userName = turbogears.identity.current.user_name
if turbogears.identity.current.user_name == userName:
personal = True
else:
personal = False
if isAdmin(turbogears.identity.current.user_name):
admin = True
else:
admin = False
user = Person.byUserName(userName)
groups = Groups.byUserName(userName)
groupsPending = Groups.byUserName(userName, unapprovedOnly=True)
groupdata={}
for g in groups:
groupdata[g] = Groups.groups(g)[g]
for g in groupsPending:
groupdata[g] = Groups.groups(g)[g]
try:
groups['cla_done']
claDone=True
except KeyError:
claDone=None
groups = sorted(groups.items(), key=itemgetter(0))
groupsPending = sorted(groupsPending.items(), key=itemgetter(0))
return dict(user=user, groups=groups, groupsPending=groupsPending, groupdata=groupdata, claDone=claDone, personal=personal, admin=admin)
@expose(template="fas.templates.user.edit")
@identity.require(turbogears.identity.not_anonymous())
def edit(self, userName=None):
'''Edit a user
'''
if not userName:
userName = turbogears.identity.current.user_name
if not canEditUser(turbogears.identity.current.user_name, userName):
turbogears.flash(_('You cannot edit %s') % userName )
userName = turbogears.identity.current.user_name
user = Person.byUserName(userName)
value = {'userName': userName,
'givenName': user.givenName,
'mail': user.mail,
'fedoraPersonBugzillaMail': user.fedoraPersonBugzillaMail,
'fedoraPersonIrcNick': user.fedoraPersonIrcNick,
'fedoraPersonKeyId': user.fedoraPersonKeyId,
'telephoneNumber': user.telephoneNumber,
'postalAddress': user.postalAddress,
'description': user.description, }
return dict(value=value)
#@validate(form=editUserForm)
@expose(template='fas.templates.editAccount')
def save(self, userName, givenName, mail, fedoraPersonBugzillaMail, telephoneNumber, postalAddress, fedoraPersonIrcNick='', fedoraPersonKeyId='', description=''):
if not canEditUser(turbogears.identity.current.user_name, userName):
turbogears.flash(_("You do not have permission to edit '%s'", userName))
turbogears.redirect('/user/edit/%s', turbogears.identity.current.user_name)
user = Person.byUserName(userName)
user.__setattr__('givenName', givenName.encode('utf8'))
user.__setattr__('mail', mail.encode('utf8'))
user.__setattr__('fedoraPersonBugzillaMail', fedoraPersonBugzillaMail.encode('utf8'))
user.__setattr__('fedoraPersonIrcNick', fedoraPersonIrcNick.encode('utf8'))
user.__setattr__('fedoraPersonKeyId', fedoraPersonKeyId.encode('utf8'))
user.__setattr__('telephoneNumber', telephoneNumber.encode('utf8'))
user.__setattr__('postalAddress', postalAddress.encode('utf8'))
user.__setattr__('description', description.encode('utf8'))
turbogears.flash(_('Your account has been updated.'))
turbogears.redirect("/user/view/%s" % userName)
return dict()
@expose(template="fas.templates.user.list")
@identity.require(turbogears.identity.in_group("accounts"))
def list(self, search="a*"):
'''List users
'''
users = Person.users(search)
try:
users[0]
except:
turbogears.flash(_("No users found matching '%s'") % search)
users = []
cla_done = Groups.byGroupName('cla_done')
claDone = {}
users.sort()
for u in users:
try:
cla_done[u]
claDone[u] = True
except KeyError:
claDone[u] = False
return dict(users=users, claDone=claDone, search=search)
@expose(template='fas.templates.user.new')
def new(self):
if turbogears.identity.not_anonymous():
turbogears.flash(_('No need to sign up, You have an account!'))
turbogears.redirect('/user/view/%s' % turbogears.identity.current.user_name)
return dict(form=newUserForm)
@validate(form=newUserForm) ## TODO: Use validate everywhere
@expose(template='fas.templates.new')
def create(self, cn, givenName, mail, telephoneNumber, postalAddress):
# TODO: Ensure that e-mails are unique?
# Also, perhaps implement a timeout- delete account
# if the e-mail is not verified (i.e. the person changes
# their password) withing X days.
import turbomail
try:
Person.newPerson(cn.encode('utf8'),
givenName.encode('utf8'),
mail.encode('utf8'),
telephoneNumber.encode('utf8'),
postalAddress.encode('utf8'))
p = Person.byUserName(cn.encode('utf8'))
newpass = p.generatePassword()
message = turbomail.Message('accounts@fedoraproject.org', p.mail, _('Fedora Project Password Reset'))
message.plain = _("You have created a new Fedora account! Your new password is: %s \nPlease go to https://admin.fedoraproject.org/fas/ to change it") % newpass['pass']
turbomail.enqueue(message)
p.__setattr__('userPassword', newpass['hash'])
turbogears.flash(_('Your password has been emailed to you. Please log in with it and change your password'))
turbogears.redirect('/login')
except ldap.ALREADY_EXISTS:
turbogears.flash(_("The username '%s' already Exists. Please choose a different username.") % cn)
turbogears.redirect('/user/new')
return dict()
@expose(template="fas.templates.user.changepass")
@identity.require(turbogears.identity.not_anonymous())
def changepass(self):
return dict()
#TODO: Validate
@expose(template="fas.templates.user.changepass")
@identity.require(turbogears.identity.not_anonymous())
def setpass(self, currentPassword, password, passwordCheck):
# TODO: use @validate/check password length
userName = turbogears.identity.current.user_name
try:
Person.auth(userName, currentPassword)
except AuthError:
turbogears.flash('Your current password did not match.')
return dict()
p = Person.byUserName(userName)
newpass = p.generatePassword(password)
try:
p.__setattr__('userPassword', newpass['hash'])
turbogears.flash(_("Your password has been changed."))
except:
turbogears.flash(_("Your password could not be changed."))
return dict()
@expose(template="fas.templates.user.resetpass")
def resetpass(self):
if turbogears.identity.not_anonymous():
turbogears.flash(_('You are already logged in!'))
turbogears.redirect('/user/view/%s' % turbogears.identity.current.user_name)
return dict()
# TODO: Validate
@expose(template="fas.templates.user.resetpass")
def sendpass(self, userName, mail):
# TODO: Do validation and check the password is long enough
import turbomail
# Logged in
if turbogears.identity.current.user_name:
turbogears.flash(_("You are already logged in."))
turbogears.redirect('/user/view/%s', turbogears.identity.current.user_name)
p = Person.byUserName(userName)
if userName and mail:
if not mail == p.mail:
turbogears.flash(_("username + email combo unknown."))
return dict()
newpass = p.generatePassword()
message = turbomail.Message('accounts@fedoraproject.org', p.mail, _('Fedora Project Password Reset'))
message.plain = _("You have requested a password reset! Your new password is - %s \nPlease go to https://admin.fedoraproject.org/fas/ to change it") % newpass['pass']
turbomail.enqueue(message)
try:
p.__setattr__('userPassword', newpass['hash'])
turbogears.flash(_('Your new password has been emailed to you.'))
turbogears.redirect('/login')
except:
turbogears.flash(_('Your password could not be reset.'))
return dict()