Email invitation

350 views
Skip to first unread message

Dwayne Blind

unread,
May 30, 2011, 11:54:40 AM5/30/11
to web2py-users
Dear all,

I am trying to implement an email invitation system.

# coding: utf8

def invitation():
form = FORM(INPUT(_name='email', requires=IS_NOT_EMPTY()),
INPUT(_type='submit'))
if form.accepts(request.vars):
uuid=str(uuid.uuid4())
db.invitation.insert(token = uuid)
mail.send(to=form.vars.email,message='click %s to
register' % URL('register',args=uuid))
redirect(URL('index'))
return dict(form=form)

I inspired myself from a code that I found on Stackoverflow but I am
new with Web2py. The code does not work. Can somebody help me ?

Thanks a lot,
Dwayne

P.S. : the indentation on Google Groups is a bit painful !

pbreit

unread,
May 30, 2011, 12:32:43 PM5/30/11
to web...@googlegroups.com
What's not working? One thing I noticed is that the URL in the email is not going to work because URL() creates a relative URL (/register/uuid). You'll need to do something like:

message='click http://mysite.com/register/%s to register' % uuid
or
message='click <a href=http://mysite.com/register/%s>register</a> to register' % uuid

Dwayne Blind

unread,
May 30, 2011, 2:37:58 PM5/30/11
to web2py-users
Thanks for your help.

This is the error message I get :

Internal error
Ticket issued: myproject/127.0.0.1.2011-05-30.20-27-41.46efa4bd-
a623-4bb9-8a5d-3579d3693c8e

Here is the traceback :

Traceback (most recent call last):
File "gluon/restricted.py", line 181, in restricted
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/
myproject/controllers/invitation.py", line 16, in <module>
File "gluon/globals.py", line 133, in <lambda>
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/
myproject/controllers/invitation.py", line 9, in invitation
UnboundLocalError: local variable 'uuid' referenced before
assignment

I don't know exactly what the uuid variable is. I understand it is
some kind of a number.

I thank you very much.

Dwayne

On 30 mai, 18:32, pbreit <pbreitenb...@gmail.com> wrote:
> What's not working? One thing I noticed is that the URL in the email is not
> going to work because URL() creates a relative URL (/register/uuid). You'll
> need to do something like:
>
> message='clickhttp://mysite.com/register/%sto register' % uuid

pbreit

unread,
May 30, 2011, 2:47:30 PM5/30/11
to web...@googlegroups.com
What does your code look like now? And line 16? Does it still include uuid=str(uuid.uuid4()) ?

pbreit

unread,
May 30, 2011, 2:48:01 PM5/30/11
to web...@googlegroups.com
Or line 9, rather.

Jonathan Lundell

unread,
May 30, 2011, 2:49:37 PM5/30/11
to web...@googlegroups.com
On May 30, 2011, at 9:32 AM, pbreit wrote:
> What's not working? One thing I noticed is that the URL in the email is not going to work because URL() creates a relative URL (/register/uuid). You'll need to do something like:

URL() takes a host argument; that should do the trick.

Dwayne Blind

unread,
May 30, 2011, 3:10:41 PM5/30/11
to web2py-users
There are 15 lines in invitation.py. And line 9 is :
"uuid=str(uuid.uuid4())".

Thanks


On 30 mai, 20:48, pbreit <pbreitenb...@gmail.com> wrote:
> Or line 9, rather.

pbreit

unread,
May 30, 2011, 3:15:21 PM5/30/11
to web...@googlegroups.com
Did you get that error before? Do you have "import uuid" in the file?

Dwayne Blind

unread,
May 30, 2011, 3:42:04 PM5/30/11
to web2py-users
Nope I don't have import uuid :(

Should I ? Where can I read more about uuid ?

Thank you

Dwayne Blind

unread,
May 30, 2011, 7:31:05 PM5/30/11
to web2py-users
I haved added "import uuid" at the top of myproject/controllers/
invitation.py, but it does not change anything.

myproject/models/invitation.py looks like this :
db.define_table('invitation',Field('token'))

myproject/views/invitation.html looks like this : {{extend
'layout.html'}} {{=form}}

Any clue ? Sorry I am just a beginner...

I have read the UUID Wikipedia page.
> > Did you get that error before? Do you have "import uuid" in the file?- Masquer le texte des messages précédents -
>
> - Afficher le texte des messages précédents -

pbreit

unread,
May 30, 2011, 8:03:26 PM5/30/11
to web...@googlegroups.com
We need to see the code and the error message.

Dwayne Blind

unread,
May 30, 2011, 8:43:31 PM5/30/11
to web2py-users
1. # coding: utf8
2. import uuid
3.
4. def invitation():
5.
6. form = FORM(INPUT(_name='email', requires=IS_NOT_EMPTY()),
7. INPUT(_type='submit'))
8.
9. if form.accepts(request.vars):
10. uuid=str(uuid.uuid4())
11. db.invitation.insert(token = uuid)
12. mail.send(to=form.vars.email,message='click %s to
register' % URL('register',args=uuid))
13. redirect(URL('index'))
14.
15. return dict(form=form)
16.

Traceback (most recent call last):
File "gluon/restricted.py", line 181, in restricted
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/
myproject/controllers/invitation.py", line 17, in <module>
File "gluon/globals.py", line 133, in <lambda>
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/
myproject/controllers/invitation.py", line 10, in invitation
UnboundLocalError: local variable 'uuid' referenced before assignment

(The traceback is the same.)

Massimo Di Pierro

unread,
May 30, 2011, 8:56:06 PM5/30/11
to web2py-users
The problem is:

import uuid

def invitation():
.... uuid ...
uuid = ...

the interpreter thinks the uuid in the function is a local one and not
the same uuid defined outside by the import.

Dwayne Blind

unread,
May 31, 2011, 4:32:48 AM5/31/11
to web...@googlegroups.com
Thank you very much Massimo.
The "import uuid" had to be added inside the "invitation" function, not outside.

Thanks a lot to all three of you.

Dwayne Blind

unread,
May 31, 2011, 2:46:42 PM5/31/11
to web...@googlegroups.com
Thank you Jonathan. I added the argument host='127.0.0.1:8000' to the URL.
 
 
Here is the final code, corrected from two typos ("uuid" instead of "token") :
 
# coding: utf8

def invitation
():

   
import uuid


    form = FORM(INPUT(_name='email', requires=IS_NOT_EMPTY
()),
       
INPUT(_type='submit'))

   
if form.accepts(request.vars):
       
uuid=str(uuid.uuid4())
       
db.invitation.insert(token = uuid)
       
mail.send(to=form.vars.email,message='click %s to register' % URL('register',args=uuid,host='127.0.0.1:8000'))
       
session.latest_invitation=form.vars.email
        redirect(URL('success_invitation'))

   
return dict(message=T('Invitation to be sent'),form=form,address=form.vars.email)

def success_invitation():
   
return dict()

def register():   # registration after invitation
   
if not db(db.invitation.token==request.args(0)).count():   # FIRST TYPO
       
redirect('error')
   
delete = lambda form:db(db.invitation.token==request.args(0)).delete()  #SECOND TYPO
   
return dict(form=auth.register(onaccept=delete))
 
The only problem I now have is to make sure that people who are not invited cannot register. How to do that ?
 
Thanks a lot,
Dwayne
 

Jonathan Lundell

unread,
May 31, 2011, 2:56:07 PM5/31/11
to web...@googlegroups.com
On May 31, 2011, at 11:46 AM, Dwayne Blind wrote:
Thank you Jonathan. I added the argument host='127.0.0.1:8000' to the URL.

There's a separate port argument that you might want to use, rather than including the port in the host. (Though in this case it doesn't make any difference, I think.)


The only problem I now have is to make sure that people who are not invited cannot register. How to do that ?

I don't have details for you, but it occurs to me that you might take advantage of the existing registration logic for email validation. Put the invitee in the database as registered with email validation pending, and use the validation step to complete the registration. IIRC, it uses a uuid to connect the response to the database entry.


Dwayne Blind

unread,
May 31, 2011, 6:43:06 PM5/31/11
to web2py-users
Thank you Jonathan for this suggestion. I will try that but any
additional help or suggestion is welcome.

Dwayne

Dwayne Blind

unread,
May 31, 2011, 8:12:52 PM5/31/11
to web2py-users
At the beginning, I was thinking of granting some permissions to the
registration page. Isn't that possible ?

Thanks for your help,
Dwayne

pbreit

unread,
May 31, 2011, 9:51:42 PM5/31/11
to web...@googlegroups.com
Maybe we should take a step back and try to understand what you are doing? You want to send out emails to some list of people? And include in that email a link which brings them back to your web site where they, and only they, can register for something?

If you are trying to learn how to program, seems OK. But if you just want to get on with your needs, have you considered Eventbrite or some other service?

Jason Brower

unread,
May 31, 2011, 11:31:25 PM5/31/11
to web...@googlegroups.com
Or interestID.
It's still in alpha, but it does invites too. :D
It's "A conference management tool for the entire event life-cycle."
It's a shameless plug cause it runs on Web2py. :D
Best Regards,
Jason Brower

Anthony

unread,
Jun 1, 2011, 2:01:36 AM6/1/11
to web...@googlegroups.com
On Tuesday, May 31, 2011 8:31:25 PM UTC-7, encompass wrote:
Or interestID.
It's still in alpha, but it does invites too. :D
It's "A conference management tool for the entire event life-cycle."
It's a shameless plug cause it runs on Web2py. :D
 
What's the URL?

Dwayne Blind

unread,
Jun 1, 2011, 9:07:15 AM6/1/11
to web...@googlegroups.com
Yes pbreit. This is what I am trying to do. I emphasize the fact that only they can register to the entire website. I am not sure that Eventbrite can help me.
 
I also would like to program it myself.
 
Dwayne

pbreit

unread,
Jun 1, 2011, 12:25:11 PM6/1/11
to web...@googlegroups.com
You could do an "onvalidation" that checks that the email address provided and/or uuid is acceptable:

Dwayne Blind

unread,
Jun 1, 2011, 8:24:51 PM6/1/11
to web...@googlegroups.com
Brilliant !
Thanks a lot. I followed your advice. This is what I did :
 
 
def my_auth_processing(form):
   
if db(db.invitation.token==request.args(0)):
       
return True
    else
:
       
return False

def
user():
   
"""
    exposes:
    http://..../[app]/default/user/login
http://..../[app]/default/user/logout
    http://..../[app]/default/user/register
    http://..../[app]/default/user/profile
    http://..../[app]/default/user/retrieve_password
    http://..../[app]/default/user/change_password
    use @auth.requires_login()
        @auth.requires_membership('group name')
        @auth.requires_permission('read','table name',record_id)
    to decorate functions that need access control
    """
   
if auth().accepts(request.vars, session, onvalidation=my_auth_processing):
       
return dict(form=auth())
   
else:
       
redirect('Registration requires invitation.')
 
Now the registration works for people who are invited. But when I log out, I can no longer click on "login" and "register". I get the error message : "404 not found". Is that easy to correct ?
 
Thanks a lot,
Dwayne
 

pbreit

unread,
Jun 1, 2011, 8:31:57 PM6/1/11
to web...@googlegroups.com

Dwayne Blind

unread,
Jun 1, 2011, 8:59:27 PM6/1/11
to web...@googlegroups.com
Thank you.

So I tried :
 
def my_auth_processing():
   
if db(db.invitation.token==request.args(0)):
       
return True
    else
:
       
return False

auth.settings.register_onvalidation = [my_auth_processing]

But it does not work. Does my_auth_processing need an argument ?
 
Here is the traceback :
 
Traceback (most recent call last):
 
File "gluon/restricted.py", line 181, in restricted
  File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/myproject/controllers/invitation.py", line 28, in <module>
 
File "gluon/globals.py", line 133, in <lambda>
 
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/myproject/controllers/invitation.py", line 26, in register
  File "gluon/tools.py", line 1657, in register
  File "gluon/sqlhtml.py", line 1031, in accepts
  File "gluon/html.py", line 1732, in accepts
TypeError: my_auth_processing() takes no arguments (1 given)
 
Thanks again,
Dwayne
 

pbreit

unread,
Jun 2, 2011, 12:58:16 AM6/2/11
to web...@googlegroups.com
This is going to be a bit trickier that I thought. I think the link in the email needs to deliver the uuid/token to a custom registration form.

This hack might work. In your user() function in default.py, add this line:

if request.args(0): form.vars.token = request.args(0)

Then the onvalidation script looks like this:

def my_auth_processing(form):
    
if db(db.invitation.token==form.vars.token):
        
return True
    else 
:
        
return False


auth.settings.register_onvalidation.append(lambda form: my_auth_processing(form))

Dwayne Blind

unread,
Jun 2, 2011, 8:15:37 AM6/2/11
to web...@googlegroups.com
Thanks. But now the logout, login and register links no longer work.
 
Traceback (most recent call last):
 
File "gluon/restricted.py", line 181, in restricted
  File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/myproject/controllers/default.py", line 55, in <module>
 
File "gluon/globals.py", line 133, in <lambda>
 
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/myproject/controllers/default.py", line 34, in user
NameError: global name 'form' is not defined
 
The problem is that the variable form is not defined in default.py. What if I replace "form" by "auth()" ?
 
Dwayne
 

Dwayne Blind

unread,
Jun 2, 2011, 8:34:00 AM6/2/11
to web...@googlegroups.com
If I put "auth()" instead of "form", everybody can register again.
 
Dwayne

Dwayne Blind

unread,
Jun 2, 2011, 10:20:28 AM6/2/11
to web...@googlegroups.com
> If I put "auth()" instead of "form", everybody can register again.
> Dwayne
 
And this is not what I want :( Can somebody help me ?
 
Dwayne

Dwayne Blind

unread,
Jun 4, 2011, 10:28:17 AM6/4/11
to web2py-users
Can somebody please help me ? I would really want this to work.

I customized db.auth_user, as described in the book, to add a 'token'
field. I just added : Field('token', writable=False, readable=False,
default='').

Then, in the controllers, I modified default.py :

def user():
db.auth_user.insert(token=request.args(1))
return dict(form=auth())

Then in the models, I modified db.py :

def my_auth_processing(form):
if db(db.auth_user.token!='')
(db.invitation.token==db.auth_user.token):
return True
else :
return False

auth.settings.register_onvalidation.append(lambda form:
my_auth_processing(form))

But unfortunately no one is prevented from registering... So this does
not work.

Dwayne

pbreit

unread,
Jun 4, 2011, 8:11:13 PM6/4/11
to web...@googlegroups.com
I should be able to take a look later today. Been out of town.

Dwayne

unread,
Jun 19, 2011, 11:01:21 AM6/19/11
to web...@googlegroups.com
Thank you pbreit. Maybe I can pay 30 dollars to the first person who finds a solution. And the solution can be posted online obviously.

Massimo Di Pierro

unread,
Jun 19, 2011, 8:09:41 PM6/19/11
to web2py-users
OK try this:

def invitation():
form = SQLFORM.factory(Field('email',requires=IS_EMAIL()))
if form.accepts(request):
db.invitation.insert(token = form.vars.email)
MESSAGE = 'click %s to register'
url = URL('user/register',host=request.env.http_host)
mail.send(to=form.vars.email, message=MESSAGE % url)
redirect('success_invitation'))
return dict(message=('Invitation to be sent'),
form=form,address=form.vars.email)

auth.settings.register_onvalidation.append(lambda form:
db.invitation(token=form.vars.email) or
form.errors.update({'email','not allowed'}))
auth.settings.register_onaccept.append(lambda form:
db(db.invitation.token==form.vars.email).delete())

Dwayne

unread,
Jun 20, 2011, 9:16:30 PM6/20/11
to web2py-users
Dear Massimo,

Thank you very much. I'll send you the 30 dollars if it works :)

I have a problem. I cannot compile :

auth.settings.register_onvalidation.append(lambda form:
db.invitation(token=form.vars.email) or
form.errors.update({'email','not allowed'}))

Why do I need : form.errors.update({'email','not allowed'}) ?

Thank you very much !

Dwayne


On 20 juin, 02:09, Massimo Di Pierro <massimo.dipie...@gmail.com>
wrote:
> OK try this:
>
> definvitation():
>     form = SQLFORM.factory(Field('email',requires=IS_EMAIL()))
>     if form.accepts(request):
>         db.invitation.insert(token = form.vars.email)
>         MESSAGE = 'click %s to register'
>         url = URL('user/register',host=request.env.http_host)
>         mail.send(to=form.vars.email, message=MESSAGE % url)
>         redirect('success_invitation'))
>     return dict(message=('Invitationto be sent'),
> > > Oops. For registration, you use this:http://web2py.com/book/default/chapter/08?search=register_onvalidation- Masquer le texte des messages précédents -

Massimo Di Pierro

unread,
Jun 20, 2011, 11:31:12 PM6/20/11
to web2py-users
My mistake, should have been

form.errors.update({'email':'not allowed'})

By adding the error message it prevent the user from registering
(because he does not have a valid token).

You do not need to pay me for help in this list but I will not refuse
a donation. ;-)

Massimo
> > > > Oops. For registration, you use this:http://web2py.com/book/default/chapter/08?search=register_onvalidation-Masquer le texte des messages précédents -

Dwayne

unread,
Jun 21, 2011, 10:37:22 AM6/21/11
to web2py-users
Thank you. So I will make a 30 dollar donation to the project.

But it still does not work.

This is what I get when I enter an email address to invite someone :

Traceback (most recent call last):
File "gluon/restricted.py", line 181, in restricted
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/
myproject/controllers/invitation.py", line 16, in <module>
File "gluon/globals.py", line 133, in <lambda>
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/
myproject/controllers/invitation.py", line 6, in invitation
File "gluon/dal.py", line 4048, in __getattr__
File "gluon/dal.py", line 4042, in __getitem__
KeyError: 'invitation'

And this is what I get when I try to register using any address :

Traceback (most recent call last):
File "gluon/restricted.py", line 181, in restricted
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/
myproject/controllers/default.py", line 50, in <module>
File "gluon/globals.py", line 133, in <lambda>
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/
myproject/controllers/default.py", line 30, in user
File "gluon/tools.py", line 1042, in __call__
File "gluon/tools.py", line 1657, in register
File "gluon/sqlhtml.py", line 1031, in accepts
File "gluon/html.py", line 1732, in accepts
File "C:/Users/Coco/Downloads/web2py_win/web2py/applications/
myproject/models/db.py", line 51, in <lambda>
File "gluon/dal.py", line 4048, in __getattr__
File "gluon/dal.py", line 4042, in __getitem__
KeyError: 'invitation'

What's the problem ? Thank you very much.

Dwayne



On 21 juin, 05:31, Massimo Di Pierro <massimo.dipie...@gmail.com>
> > > > > Oops. For registration, you use this:http://web2py.com/book/default/chapter/08?search=register_onvalidatio...le texte des messages précédents -
>
> > > - Afficher le texte des messages précédents -- Masquer le texte des messages précédents -
Reply all
Reply to author
Forward
0 new messages