Merge branch 'master' of ssh://git.fedorahosted.org/git/fedora-infrastructure
This commit is contained in:
commit
486f631b27
10 changed files with 203 additions and 41 deletions
16
fas/TODO
16
fas/TODO
|
@ -1,14 +1,6 @@
|
||||||
Things to Fix in FAS2 before declaring it done
|
Things to Fix in FAS2 before declaring it done:
|
||||||
|
|
||||||
|
* fasClient.py: Proper logging
|
||||||
|
|
||||||
safasprovider.py
|
Nice-to-have things:
|
||||||
----------------
|
* fas/group.py: Easy searching within groups (and sponsor/admin interface)
|
||||||
validate_password():
|
|
||||||
We'll want to change this to something that allows for longer passwords
|
|
||||||
(like md5). The one thing about that is we have to figure out how system
|
|
||||||
passwords use salt, etc. That way we'll be able to use this with
|
|
||||||
make_shell_accounts.
|
|
||||||
|
|
||||||
fasClient.py
|
|
||||||
---------------
|
|
||||||
Proper logging
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ tg.strict_parameters = True
|
||||||
|
|
||||||
server.webpath='/accounts'
|
server.webpath='/accounts'
|
||||||
base_url_filter.on=True
|
base_url_filter.on=True
|
||||||
base_url_filter.base_url = "https://publictest10.fedoraproject.org/accounts"
|
base_url_filter.base_url = "https://publictest10.fedoraproject.org"
|
||||||
|
|
||||||
# Make the session cookie only return to the host over an SSL link
|
# Make the session cookie only return to the host over an SSL link
|
||||||
# Disabled for testing.
|
# Disabled for testing.
|
||||||
|
|
|
@ -314,7 +314,7 @@ class Groups(SABase):
|
||||||
A class method that can be used to search groups
|
A class method that can be used to search groups
|
||||||
based on their email addresses since it is unique.
|
based on their email addresses since it is unique.
|
||||||
'''
|
'''
|
||||||
return cls.query.join(['group_email_purposes', 'group_email']).filter_by(email=email).one()
|
return cls.query.join(['email_purposes', 'group_email']).filter_by(email=email).one()
|
||||||
|
|
||||||
by_email_address = classmethod(by_email_address)
|
by_email_address = classmethod(by_email_address)
|
||||||
|
|
||||||
|
@ -423,15 +423,15 @@ mapper(People, PeopleTable, properties = {
|
||||||
'email_purposes': relation(EmailPurposes, backref = 'person',
|
'email_purposes': relation(EmailPurposes, backref = 'person',
|
||||||
collection_class = column_mapped_collection(
|
collection_class = column_mapped_collection(
|
||||||
EmailPurposesTable.c.purpose)),
|
EmailPurposesTable.c.purpose)),
|
||||||
|
'person_emails': relation(PersonEmails, backref = 'person',
|
||||||
|
collection_class = column_mapped_collection(
|
||||||
|
PersonEmailsTable.c.email)),
|
||||||
'approved_roles': relation(ApprovedRoles, backref='member',
|
'approved_roles': relation(ApprovedRoles, backref='member',
|
||||||
primaryjoin = PeopleTable.c.id==ApprovedRoles.c.person_id),
|
primaryjoin = PeopleTable.c.id==ApprovedRoles.c.person_id),
|
||||||
'unapproved_roles': relation(UnApprovedRoles, backref='member',
|
'unapproved_roles': relation(UnApprovedRoles, backref='member',
|
||||||
primaryjoin = PeopleTable.c.id==UnApprovedRoles.c.person_id)
|
primaryjoin = PeopleTable.c.id==UnApprovedRoles.c.person_id)
|
||||||
})
|
})
|
||||||
mapper(PersonEmails, PersonEmailsTable, properties = {
|
mapper(PersonEmails, PersonEmailsTable)
|
||||||
'person': relation(People, backref = 'person_emails', uselist = False,
|
|
||||||
primaryjoin = PeopleTable.c.id==PersonEmailsTable.c.person_id)
|
|
||||||
})
|
|
||||||
mapper(EmailPurposes, EmailPurposesTable, properties = {
|
mapper(EmailPurposes, EmailPurposesTable, properties = {
|
||||||
'person_email': relation(PersonEmails, uselist = False,
|
'person_email': relation(PersonEmails, uselist = False,
|
||||||
primaryjoin = PersonEmailsTable.c.id==EmailPurposesTable.c.email_id)
|
primaryjoin = PersonEmailsTable.c.id==EmailPurposesTable.c.email_id)
|
||||||
|
@ -452,13 +452,13 @@ mapper(Groups, GroupsTable, properties = {
|
||||||
'email_purposes': relation(GroupEmailPurposes, backref = 'group',
|
'email_purposes': relation(GroupEmailPurposes, backref = 'group',
|
||||||
collection_class = column_mapped_collection(
|
collection_class = column_mapped_collection(
|
||||||
GroupEmailPurposesTable.c.purpose)),
|
GroupEmailPurposesTable.c.purpose)),
|
||||||
|
'group_emails': relation(GroupEmails, backref = 'group',
|
||||||
|
collection_class = column_mapped_collection(
|
||||||
|
GroupEmailsTable.c.email)),
|
||||||
'prerequisite': relation(Groups, uselist=False,
|
'prerequisite': relation(Groups, uselist=False,
|
||||||
primaryjoin = GroupsTable.c.prerequisite_id==GroupsTable.c.id)
|
primaryjoin = GroupsTable.c.prerequisite_id==GroupsTable.c.id)
|
||||||
})
|
})
|
||||||
mapper(GroupEmails, GroupEmailsTable, properties = {
|
mapper(GroupEmails, GroupEmailsTable)
|
||||||
'group': relation(Groups, backref = 'group_emails', uselist = False,
|
|
||||||
primaryjoin = GroupsTable.c.id==GroupEmailsTable.c.group_id)
|
|
||||||
})
|
|
||||||
mapper(GroupEmailPurposes, GroupEmailPurposesTable, properties = {
|
mapper(GroupEmailPurposes, GroupEmailPurposesTable, properties = {
|
||||||
'group_email': relation(GroupEmails, uselist = False,
|
'group_email': relation(GroupEmails, uselist = False,
|
||||||
primaryjoin = GroupEmailsTable.c.id==GroupEmailPurposesTable.c.email_id)
|
primaryjoin = GroupEmailsTable.c.id==GroupEmailPurposesTable.c.email_id)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<h3>The Fedora Project
|
<h3>The Fedora Project
|
||||||
Individual Contributor License Agreement (CLA)
|
Individual Contributor License Agreement (CLA)
|
||||||
</h3>
|
</h3>
|
||||||
<a href="http://www.fedora.redhat.com/licenses/">http://www.fedora.redhat.com/licenses/</a>
|
<a href="http://fedoraproject.org/wiki/Legal/Licenses/CLA">http://fedoraproject.org/wiki/Legal/Licenses/CLA</a>
|
||||||
<p>
|
<p>
|
||||||
Thank you for your interest in The Fedora Project (the "Project"). In order to clarify the intellectual property license granted with Contributions from any person or entity, Red hat, Inc. ("Red Hat"), as maintainer of the Project, must have a Contributor License Agreement (CLA) on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for Your protection as a Contributor as well as the protection of the Project and its users; it does not change your rights to use your own Contributions for any other purpose.
|
Thank you for your interest in The Fedora Project (the "Project"). In order to clarify the intellectual property license granted with Contributions from any person or entity, Red hat, Inc. ("Red Hat"), as maintainer of the Project, must have a Contributor License Agreement (CLA) on file that has been signed by each Contributor, indicating agreement to the license terms below. This license is for Your protection as a Contributor as well as the protection of the Project and its users; it does not change your rights to use your own Contributions for any other purpose.
|
||||||
</p>
|
</p>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
The Fedora Project
|
The Fedora Project
|
||||||
Individual Contributor License Agreement (CLA)
|
Individual Contributor License Agreement (CLA)
|
||||||
http://www.fedora.redhat.com/licenses/
|
http://fedoraproject.org/wiki/Legal/Licenses/CLA
|
||||||
|
|
||||||
Thank you for your interest in The Fedora Project (the
|
Thank you for your interest in The Fedora Project (the
|
||||||
"Project"). In order to clarify the intellectual property license
|
"Project"). In order to clarify the intellectual property license
|
||||||
|
|
30
fas/fas/templates/user/email/add.html
Normal file
30
fas/fas/templates/user/email/add.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<!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>${_('Add Email')}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>${_('Add Email')}</h2>
|
||||||
|
<form action="${tg.url('/user/email/save/%s' % target.username)}" method="post" enctype="multipart/form-data">
|
||||||
|
<div class="field">
|
||||||
|
<label for="email">${_('Email')}:</label>
|
||||||
|
<input type="text" id="email" name="email" />
|
||||||
|
<!-- TODO: More generic documentation for adding an email -->
|
||||||
|
<script type="text/javascript">var hb1 = new HelpBalloon({dataURL: '${tg.url('/help/get_help/user_primary_email')}'});</script>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<label for="description">${_('Description')}:</label>
|
||||||
|
<input type="text" id="description" name="description" />
|
||||||
|
<!-- TODO: Correct documentation for this -->
|
||||||
|
<script type="text/javascript">var hb1 = new HelpBalloon({dataURL: '${tg.url('/help/get_help/user_primary_email')}'});</script>
|
||||||
|
</div>
|
||||||
|
<div class="field">
|
||||||
|
<input type="submit" value="${_('Save!')}" />
|
||||||
|
<a href="${tg.url('/user/email/manage')}">${_('Cancel')}</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -7,7 +7,7 @@
|
||||||
<title>${_('Manage Emails')}</title>
|
<title>${_('Manage Emails')}</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>${_('Managing Emails for %s') % person.username}</h2>
|
<h2>${_('Managing Emails for %s') % target.username}</h2>
|
||||||
<h3>Available Emails</h3>
|
<h3>Available Emails</h3>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -18,7 +18,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr py:for="email in person.person_emails">
|
<tr py:for="email in target.person_emails.values()">
|
||||||
<td><a href="mailto:${email.email}">${email.email}</a></td>
|
<td><a href="mailto:${email.email}">${email.email}</a></td>
|
||||||
<td>${email.description}</td>
|
<td>${email.description}</td>
|
||||||
<td py:if="email.verified"><span class="approved">${_('Verified')}</span></td>
|
<td py:if="email.verified"><span class="approved">${_('Verified')}</span></td>
|
||||||
|
@ -36,7 +36,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr py:for="purpose in person.email_purposes.values()">
|
<tr py:for="purpose in target.email_purposes.values()">
|
||||||
<td><a href="mailto:${purpose.email}">${purpose.email}</a></td>
|
<td><a href="mailto:${purpose.email}">${purpose.email}</a></td>
|
||||||
<td>${purpose.person_email.description}</td>
|
<td>${purpose.person_email.description}</td>
|
||||||
<td>${purpose.purpose}</td>
|
<td>${purpose.purpose}</td>
|
||||||
|
|
|
@ -3,6 +3,8 @@ from turbogears import controllers, expose, paginate, identity, redirect, widget
|
||||||
from turbogears.database import session
|
from turbogears.database import session
|
||||||
import cherrypy
|
import cherrypy
|
||||||
|
|
||||||
|
import turbomail
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import gpgme
|
import gpgme
|
||||||
|
@ -221,7 +223,8 @@ class User(controllers.Controller):
|
||||||
target = person
|
target = person
|
||||||
if not canEditUser(person, target):
|
if not canEditUser(person, target):
|
||||||
turbogears.flash(_('You cannot edit %s') % target.username )
|
turbogears.flash(_('You cannot edit %s') % target.username )
|
||||||
username = turbogears.identity.current.username
|
turbogears.redirect('/user/view/%s', target.username)
|
||||||
|
return dict()
|
||||||
return dict(target=target)
|
return dict(target=target)
|
||||||
|
|
||||||
@identity.require(turbogears.identity.not_anonymous())
|
@identity.require(turbogears.identity.not_anonymous())
|
||||||
|
@ -236,7 +239,7 @@ class User(controllers.Controller):
|
||||||
|
|
||||||
if not canEditUser(person, target):
|
if not canEditUser(person, target):
|
||||||
turbogears.flash(_("You do not have permission to edit '%s'") % target.username)
|
turbogears.flash(_("You do not have permission to edit '%s'") % target.username)
|
||||||
turbogears.redirect('/user/edit/%s', target.username)
|
turbogears.redirect('/user/view/%s', target.username)
|
||||||
return dict()
|
return dict()
|
||||||
try:
|
try:
|
||||||
target.human_name = human_name
|
target.human_name = human_name
|
||||||
|
@ -286,7 +289,6 @@ class User(controllers.Controller):
|
||||||
# Also, perhaps implement a timeout- delete account
|
# Also, perhaps implement a timeout- delete account
|
||||||
# if the e-mail is not verified (i.e. the person changes
|
# if the e-mail is not verified (i.e. the person changes
|
||||||
# their password) withing X days.
|
# their password) withing X days.
|
||||||
import turbomail
|
|
||||||
try:
|
try:
|
||||||
person = People()
|
person = People()
|
||||||
person.username = username
|
person.username = username
|
||||||
|
@ -296,6 +298,7 @@ class User(controllers.Controller):
|
||||||
person.status = 'active'
|
person.status = 'active'
|
||||||
session.flush()
|
session.flush()
|
||||||
|
|
||||||
|
# TODO: Handle properly if email has already been used. This might be painful, since the person already exists, at this point.
|
||||||
person_email = PersonEmails()
|
person_email = PersonEmails()
|
||||||
person_email.email = email
|
person_email.email = email
|
||||||
person_email.person = person
|
person_email.person = person
|
||||||
|
|
|
@ -3,11 +3,16 @@ from turbogears import controllers, expose, paginate, identity, redirect, widget
|
||||||
from turbogears.database import session
|
from turbogears.database import session
|
||||||
import cherrypy
|
import cherrypy
|
||||||
|
|
||||||
|
import turbomail
|
||||||
|
import random
|
||||||
|
|
||||||
from fas.model import People
|
from fas.model import People
|
||||||
from fas.model import PersonEmails
|
from fas.model import PersonEmails
|
||||||
from fas.model import EmailPurposes
|
from fas.model import EmailPurposes
|
||||||
from fas.model import Log
|
from fas.model import Log
|
||||||
|
|
||||||
|
from fas.auth import *
|
||||||
|
|
||||||
class NonFedoraEmail(validators.FancyValidator):
|
class NonFedoraEmail(validators.FancyValidator):
|
||||||
'''Make sure that an email address is not @fedoraproject.org'''
|
'''Make sure that an email address is not @fedoraproject.org'''
|
||||||
def _to_python(self, value, state):
|
def _to_python(self, value, state):
|
||||||
|
@ -16,13 +21,20 @@ class NonFedoraEmail(validators.FancyValidator):
|
||||||
if value.endswith('@fedoraproject.org'):
|
if value.endswith('@fedoraproject.org'):
|
||||||
raise validators.Invalid(_("To prevent email loops, your email address cannot be @fedoraproject.org."), value, state)
|
raise validators.Invalid(_("To prevent email loops, your email address cannot be @fedoraproject.org."), value, state)
|
||||||
|
|
||||||
class EmailCreate(validators.Schema):
|
class EmailSave(validators.Schema):
|
||||||
email = validators.All(
|
email = validators.All(
|
||||||
validators.Email(not_empty=True, strip=True),
|
validators.Email(not_empty=True, strip=True),
|
||||||
NonFedoraEmail(not_empty=True, strip=True),
|
NonFedoraEmail(not_empty=True, strip=True),
|
||||||
)
|
)
|
||||||
#fedoraPersonBugzillaMail = validators.Email(strip=True)
|
description = validators.String(not_empty=True, max=512)
|
||||||
postal_address = validators.String(max=512)
|
|
||||||
|
def generate_validtoken(length=32):
|
||||||
|
''' Generate Validation Token '''
|
||||||
|
chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
token = ''
|
||||||
|
for i in xrange(length):
|
||||||
|
token += random.choice(chars)
|
||||||
|
return token
|
||||||
|
|
||||||
class Email(controllers.Controller):
|
class Email(controllers.Controller):
|
||||||
|
|
||||||
|
@ -34,7 +46,7 @@ class Email(controllers.Controller):
|
||||||
def index(self):
|
def index(self):
|
||||||
'''Redirect to manage
|
'''Redirect to manage
|
||||||
'''
|
'''
|
||||||
turbogears.redirect('/user/email/manage/%s' % turbogears.identity.current.user_name)
|
turbogears.redirect('/user/email/manage')
|
||||||
|
|
||||||
|
|
||||||
@expose(template="fas.templates.error")
|
@expose(template="fas.templates.error")
|
||||||
|
@ -48,12 +60,135 @@ class Email(controllers.Controller):
|
||||||
#@validate(validators=UserView())
|
#@validate(validators=UserView())
|
||||||
@error_handler(error)
|
@error_handler(error)
|
||||||
@expose(template="fas.templates.user.email.manage", allow_json=True)
|
@expose(template="fas.templates.user.email.manage", allow_json=True)
|
||||||
def manage(self, username=None):
|
def manage(self, targetname=None):
|
||||||
'''
|
'''
|
||||||
Manage a person's emails.
|
Manage a person's emails
|
||||||
'''
|
'''
|
||||||
if not username:
|
# TODO: Some sort of auth checking - other people should
|
||||||
username = turbogears.identity.current.user_name
|
# probably be limited to looking at a person's email through
|
||||||
|
# /user/view, although admins should probably be able to set
|
||||||
|
# emails (with/without verification?)
|
||||||
|
username = turbogears.identity.current.user_name
|
||||||
person = People.by_username(username)
|
person = People.by_username(username)
|
||||||
return dict(person=person)
|
|
||||||
|
if targetname:
|
||||||
|
target = People.by_username(targetname)
|
||||||
|
else:
|
||||||
|
target = person
|
||||||
|
|
||||||
|
return dict(target=target)
|
||||||
|
|
||||||
|
@identity.require(turbogears.identity.not_anonymous())
|
||||||
|
#@validate(validators=UserView())
|
||||||
|
@error_handler(error)
|
||||||
|
@expose(template="fas.templates.user.email.add", allow_json=True)
|
||||||
|
def add(self, targetname=None):
|
||||||
|
'''
|
||||||
|
Display the form to add an email
|
||||||
|
'''
|
||||||
|
username = turbogears.identity.current.user_name
|
||||||
|
person = People.by_username(username)
|
||||||
|
|
||||||
|
if targetname:
|
||||||
|
target = People.by_username(targetname)
|
||||||
|
else:
|
||||||
|
target = person
|
||||||
|
|
||||||
|
if not canEditUser(person, target):
|
||||||
|
turbogears.flash(_('You cannot edit %s') % target.username )
|
||||||
|
turbogears.redirect('/user/email/manage')
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
return dict(target=target)
|
||||||
|
|
||||||
|
@identity.require(turbogears.identity.not_anonymous())
|
||||||
|
@validate(validators=EmailSave())
|
||||||
|
@error_handler(error)
|
||||||
|
@expose(template="fas.templates.user.email.add", allow_json=True)
|
||||||
|
def save(self, targetname, email, description):
|
||||||
|
'''
|
||||||
|
Display the form to add an email
|
||||||
|
'''
|
||||||
|
username = turbogears.identity.current.user_name
|
||||||
|
person = People.by_username(username)
|
||||||
|
|
||||||
|
if targetname:
|
||||||
|
target = People.by_username(targetname)
|
||||||
|
else:
|
||||||
|
target = person
|
||||||
|
|
||||||
|
if not canEditUser(person, target):
|
||||||
|
turbogears.flash(_('You cannot edit %s') % target.username )
|
||||||
|
turbogears.redirect('/user/email/manage')
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
validtoken = generate_validtoken()
|
||||||
|
|
||||||
|
try:
|
||||||
|
person_email = PersonEmails()
|
||||||
|
person_email.email = email
|
||||||
|
person_email.person = target
|
||||||
|
person_email.description = description
|
||||||
|
person_email.validtoken = validtoken
|
||||||
|
session.flush()
|
||||||
|
# Hmm, should this be checked in the validator or here?
|
||||||
|
except IntegrityError:
|
||||||
|
turbogears.flash(_('The email \'%s\' is already in used.') % email)
|
||||||
|
return dict(target=target)
|
||||||
|
else:
|
||||||
|
# TODO: Make this email more friendly. Maybe escape the @ in email too?
|
||||||
|
validurl = config.get('base_url_filter.base_url') + turbogears.url('/user/email/verify/%s/%s/%s') % (target.username, email, validtoken)
|
||||||
|
message = turbomail.Message(config.get('accounts_mail'), email, _('Confirm this email address'))
|
||||||
|
message.plain = _('''
|
||||||
|
Go to this URL to verify that you own this email address: %s
|
||||||
|
''') % validurl
|
||||||
|
turbomail.enqueue(message)
|
||||||
|
turbogears.flash(_('Your email has been added. Before you can use this email, you must verify it. The email you added should receive a message with instructions shortly.'))
|
||||||
|
|
||||||
|
return dict(target=target)
|
||||||
|
|
||||||
|
return dict(target=target)
|
||||||
|
|
||||||
|
@identity.require(turbogears.identity.not_anonymous())
|
||||||
|
# TODO: Validation!
|
||||||
|
#@validate(validators=UserView())
|
||||||
|
@error_handler(error)
|
||||||
|
@expose(allow_json=True)
|
||||||
|
def verify(self, targetname, email, validtoken):
|
||||||
|
'''
|
||||||
|
Verify an email
|
||||||
|
'''
|
||||||
|
username = turbogears.identity.current.user_name
|
||||||
|
person = People.by_username(username)
|
||||||
|
|
||||||
|
if targetname:
|
||||||
|
target = People.by_username(targetname)
|
||||||
|
else:
|
||||||
|
target = person
|
||||||
|
|
||||||
|
if not canEditUser(person, target):
|
||||||
|
turbogears.flash(_('You cannot edit %s') % target.username )
|
||||||
|
turbogears.redirect('/user/email/manage')
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
if target.person_emails[email].verified:
|
||||||
|
turbogears.flash(_('The email provided has already been verified.'))
|
||||||
|
turbogears.redirect('/user/email/manage')
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
try:
|
||||||
|
if target.person_emails[email].validtoken == validtoken:
|
||||||
|
target.person_emails[email].validtoken = ''
|
||||||
|
target.person_emails[email].verified = True
|
||||||
|
turbogears.flash(_('Your email has been successfully verified.'))
|
||||||
|
turbogears.redirect('/user/email/manage')
|
||||||
|
return dict()
|
||||||
|
else:
|
||||||
|
turbogears.flash(_('The verification string did not match.'))
|
||||||
|
turbogears.redirect('/user/email/manage')
|
||||||
|
return dict()
|
||||||
|
except KeyError:
|
||||||
|
turbogears.flash(_('No such email is associated with your user.'))
|
||||||
|
turbogears.redirect('/user/email/manage')
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
|
|
@ -71,11 +71,12 @@ cluster people_status_idx on people;
|
||||||
|
|
||||||
CREATE TABLE person_emails (
|
CREATE TABLE person_emails (
|
||||||
id serial primary key,
|
id serial primary key,
|
||||||
email text not null unique,
|
email text not null,
|
||||||
person_id INTEGER NOT NULL references people(id),
|
person_id INTEGER NOT NULL references people(id),
|
||||||
validtoken text,
|
validtoken text,
|
||||||
description text,
|
description text,
|
||||||
verified boolean NOT NULL DEFAULT false
|
verified boolean NOT NULL DEFAULT false
|
||||||
|
unique (email, verified) --You can't "claim" an email before you verify it first
|
||||||
);
|
);
|
||||||
|
|
||||||
create index person_emails_person_id_idx on person_emails(person_id);
|
create index person_emails_person_id_idx on person_emails(person_id);
|
||||||
|
@ -141,11 +142,12 @@ cluster groups_group_type_idx on groups;
|
||||||
--
|
--
|
||||||
CREATE TABLE group_emails (
|
CREATE TABLE group_emails (
|
||||||
id serial primary key,
|
id serial primary key,
|
||||||
email text not null unique,
|
email text not null,
|
||||||
group_id INTEGER NOT NULL references groups(id),
|
group_id INTEGER NOT NULL references groups(id),
|
||||||
validtoken text,
|
validtoken text,
|
||||||
description text,
|
description text,
|
||||||
verified boolean NOT NULL DEFAULT false
|
verified boolean NOT NULL DEFAULT false
|
||||||
|
unique (email, verified) --You can't "claim" an email before you verify it first
|
||||||
);
|
);
|
||||||
|
|
||||||
create index group_emails_group_id_idx on group_emails(group_id);
|
create index group_emails_group_id_idx on group_emails(group_id);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue